Scoping question
floren opened this issue · 13 comments
I'm not particularly good with dynamic scoping, I've pretty much never used it, so I've got some questions about how I should be writing my code. Basically, any variables I use in functions are now "off-limits" from using elsewhere... here's a simplified example:
var strconv = import("strconv")
func parseBase10(s) {
r, _ = strconv.ParseInt(s, 10, 64)
return r
}
r = ["5", "4", "3"]
for i = 0; i < len(r); i++ {
println(parseBase10(r[i])
}
The call to parseBase10 changes r from a slice of strings to an int64. Whoops! I thought I might do this:
func parseBase10(s) {
var r, err = strconv.ParseInt(s, 10, 64)
return r
}
But that doesn't work quite right:
> parseBase10("5")
[]interface {}{5, interface {}(nil)}
What do I do in this situation? Start using more unique variable names? Or is there a correct way to use 'var' in that sort of multiple assignment situation?
In Javascript, can see the scope/output is different
https://jsfiddle.net/kg35efrk/
var divData = document.getElementById('divData');
divData.innerHTML = "";
function test(s) {
var r = s + "z"
return r
}
var r = "a"
divData.innerHTML += r + "<br />";
divData.innerHTML += test("b") + "<br />";
divData.innerHTML += r + "<br />";
Output:
a
bz
a
In Anko:
package main
import (
"fmt"
"log"
"github.com/mattn/anko/vm"
)
func main() {
env := vm.NewEnv()
err := env.Define("println", fmt.Println)
if err != nil {
log.Fatalf("Define error: %v\n", err)
}
script := `
func test(s) {
r = s + "z"
return r
}
r = "a"
println(r)
println(test(r))
println(r)
`
_, err = env.Execute(script)
if err != nil {
log.Fatalf("execute error: %v\n", err)
}
}
Output:
a
az
az
So can see the scope is not acting the same in Anko as in JavaScript.
You can replicate the Javascript behavior by saying "var r" in the function (http://play-anko.appspot.com/p/f5c402dab08863dd97bf57d0409063085ece26b1)
func test(s) {
var r = s + "z"
return r
}
r = "a"
println(r)
println(test(r))
println(r)
But using var in that manner doesn't work if you are trying to set two variables at once:
var r, err = strconv.ParseInt("5", 10, 64)
That simply creates r as a []interface containing 5 and nil (the result and the error).
Can get the same output either way by the following:
Case 1:
JavaScript:
var divData = document.getElementById('divData');
divData.innerHTML = "";
function test(s) {
r = s + "z"
return r
}
var r = "a"
divData.innerHTML += r + "<br />";
divData.innerHTML += test("b") + "<br />";
divData.innerHTML += r + "<br />";
Anko
func test(s) {
r = s + "z"
return r
}
r = "a"
println(r)
println(test("b"))
println(r)
Output:
a
bz
bz
Case 2:
JavaScript:
var divData = document.getElementById('divData');
divData.innerHTML = "";
function test(s) {
var r = s + "z"
return r
}
var r = "a"
divData.innerHTML += r + "<br />";
divData.innerHTML += test("b") + "<br />";
divData.innerHTML += r + "<br />";
Anko:
func test(s) {
var r = s + "z"
return r
}
var r = "a"
println(r)
println(test("b"))
println(r)
Output:
a
bz
a
Yes, I think we're talking past each other here. I want to use 'var' to isolate the values returned from a multiple return function such as strconv.ParseInt (simply a convenient example). However it doesn't work to say:
var r, err
r, err = strconv.ParseInt(s, 10, 64)
nor does it work to say
var r, err = strconv.ParseInt(s, 10, 64)
I can see no way to reproduce in the multiple return case what your examples demonstrate for the single case.
I think this works and is what you want.
strconv = import("strconv")
func test(s) {
var r = 0
r, _ = strconv.ParseInt(s, 10, 64)
return r
}
var r = "1"
println(r)
println(test("2"))
println(r)
Output:
1
2
1
That being said, I think there is a bug with var multiple case.
strconv = import("strconv")
func test(s) {
var r, err = strconv.ParseInt(s, 10, 64)
return r
}
var r = "1"
println(r)
println(test("2"))
println(r)
Output:
1
[2 <nil>]
1
But I would expect the output to be:
1
2
1
Yep, you've hit the nail on the head! I was concerned I might just be doing it wrong, but I think there is indeed a bug in the behavior of var.
No.
var r, err = strconv.ParseInt(s, 10, 64)
This must be
var r = 0
var err = nil
r, err = strconv.ParseInt(s, 10, 64)
Doing it that way requires knowledge of the types returned by ParseInt, in a dynamically-typed system that tries to avoid exposing types.
Can't we update ast.VarStmt to be similar to ast.LetsStmt so that var r, err = strconv.ParseInt(s, 10, 64)
will work?
Yes, but we must consider it to be separated to
var a, b
define a and b
var a, b = something()
define a and b, and assign both from something()
Does PR #275 fix this issue?
Looks like it should, I'll have to test when I get a moment but the tests & code look right
Close this?