Running the following snippet of Go code one can realize that function foo receives the value of the first argument actually set while evaluating the second argument of the function. This behavior might look counterintuitive so that we need to prove this to be a part of language spec, not something implementation-specific.
package main
import (
"fmt"
)
func setVal(s *int, v int) int {
old := *s
*s = v
return old
}
func foo(s int, p int) {
fmt.Printf("s = %d, p = %d\n", s, p)
}
func main() {
var s int
foo(s, setVal(&s, 99))
}
Programm outputs s = 99, p = 0, which means a modified value of variable s has been passed to the function.
Here is what the Go spec says regarding the case.
In a function call, ...arguments must be single-valued expressions ... arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function... Where usual order is the lexical left-to-right order.
A variable is a storage location for holding a value. ...A variable's value is retrieved by referring to the variable in an expression; it is the most recent value assigned to the variable.
Therefore foo(s, setVal(&s, 99)) is a function call, variable s and function setVal() are the single-valued expressions, and s is evaluated first. The last spec statement makes one assume the result of a variable evaluation is its value, so if that is true, function foo should receive initial value of the variable s.
But it in fact it appears that the function receives the value of the first argument been set at the moment of evaluating the second argument, which is a bit confusing.
Does that mean the evaluation order is broken or the result of a variable evaluation is not its value?
What you "miss" from the spec is Spec: Calls:
In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.
Evaluating the parameters does not mean their values are read or "taken". The first parameter is s, its evaluation is s itself, but its value is not yet read. The second parameter is evaluated, which means setVal() is called and will modify the value of s.
Now that we have evaluated the parameters, their values are read, so s will have the value 99.
Evaluating s in the example is trivial, but of course that could be a more complex expression just like the second argument. Here's a more complex example:
s, s2 := new(int), new(int)
getFunc := func() func(s int, p int) { return foo }
first := func(a, b *int) *int { return a }
getFunc()(*first(s, s2), setVal(s, 99))
Call of the last function involves the following steps:
function value is evaluated: getFunc() is called, it's return value will be the function value
parameters are evaluated:
(a) first() is called, its return value is dereferenced;
(b) setVal() is called, its return value will be used
And now the values are taken: value of *s and the old value of s (value returned by setVal()).
This will output the same as your example, try it on the Go Playground.
Related
I'm coming from an Scala background, and in Scala, you could define functions both as a single value, or an actual function, for instance:
val inc1: Int => Int = _ + 1 // single FUNCTION value
def inc2(x: Int): Int = x + 1 // normal function definition
// in this case "inc1 eq inc1" is true, since this is a single instance
// but "inc2 eq inc2" is false
And these 2 have some differences (i.e., size allocation, first one is a single instance, while the other one returns an instance each time it is invoked, ...), so based on the use case, we could kind of reason which one to use. Now I'm new to golang, and wanted to know if the below 2 function definitions (correct me if I'm wrong with the phrase) differ in Golang, and if so, what are differences?
var inc1 = func(x int) int { return x + 1 }
func inc2(x int) int { return x + 1 }
Thanks in advance!
Scala borrows a lot from functional programming. Go does not.
(If you've used multiple other programming languages, you should definitely read the Go specification. It's not very long as Go is not a very large language, although the new generics definitely complicate things a bit.)
In Go, the func keyword introduces a function definition or function type, with the details being context-dependent. The var keyword introduces a variable declaration.1 So:
func inc2(x int) int { return x + 1 }
defines a function, inc2, whose code is as shown. But:
var inc1 = // ...
declares and then initializes a variable, inc1. The type and initial value of the variable are determined by the commented-out section, so:
var inc1 = func(x int) int { return x + 1 }
defines a function (with no name) whose code is as shown. That function is then assigned to the variable as its initial value, so that the implied type of the variable is func (int) int or function taking one argument of type int and returning one value of type int.
Having created a variable, you can now either call the function currently stored in that variable:
func callit(arg int) {
result := inc1(arg)
// ... do something with the result ...
}
Or you can assign a new value into the variable, e.g.:
func overwrite() {
inc1 = func(a int) int { return a * 2 } // name `inc1` is now misleading
}
Because inc2 is a function, you can't re-assign a new value to it: it's just a function, not a variable.
1Note that a variable declaration with an initialization can use the "short declaration" form:
func f() {
v := 3
// ...
}
where we leave out the type and just say "use the type of the expression to figure out the type of the declaration". This declares and initializes the variable. Short declarations can only appear in block scope, so these must be inside some function. Other than omitting the var keyword they do nothing that you couldn't do by including the var keyword, or sometimes multiple var keywords:
result, err := doit()
might require:
var result someType
var err error
result, err = doit()
when written without using the short-declaration form.
I am unable to understand to why value of x increase even after scope end for varaiable ‘x’ in function foo(). It’s must reset to zero everytime I called a function. This this not happening why?
package main
import "fmt"
func main() {
a := foo()
fmt.Printf("%d\n", a())
fmt.Printf("%d\n", a())
fmt.Printf("%d\n", a())
}
func foo() func() int {
var x int // value should reset to zero
return func() int {
x++
return x
}
}
Go Playground
Your function foo returns a new function each time it's called. foo doesn't just create that function though, it also allocates memory for a variable called x of type int. That variable is declared in the scope of foo, and as such, the function foo returns has access to that variable.
In essence, you're returning what is often referred to as a closure. A function that has access to a scope (variables) that are invisible to any other code. To visually represent this, maybe thing of it like this:
The function is what you return, the "environment record" it holds on to is the function body of foo, and all of the variables declared there (in your case x). The next environment record would be the package (variables declared outside functions with var).
So what happens exactly:
a := foo()
Here, foo allocates variable x, and returns a function that holds on to this new variable.
a() // returns 1
a() // returns 2
The x variable created by foo is incremented, and its new value is returned each time.
Now that you know this, you should be able to work out what the output here will be:
a, b := foo(), foo() // creates 2 functions, 2 variables called x
_ = a()
_ = a()
fmt.Printf("a call returned %d\nb call returned %d\n", a(), b())
The output should be "a call returned 3 b call returned 1"
Golang supports assigning multiple return values to multiple left hand side variables. E.g:
func test() (string, string) {
return "1", "1"
}
a, b := test()
or
a, _ := test()
and the number of receiving variables and return values must match:
b = test() //wrong
But for some built-in types, such as [] or <-, a variable number of return values are supported
key, exist := map[key]
key := map[key]
I can read value from channel like this
c <- myChan
c, exist <- myChan
How can we explain the inconsistency? Is this a capability reserved to the core go runtime/language?
This behavior clearly specified in the golang specification:
Receive operator
A receive expression used in an assignment or initialization of the special form
x, ok = <-ch
x, ok := <-ch
var x, ok = <-ch
var x, ok T = <-ch
yields an additional untyped boolean result reporting whether the communication succeeded. The value of ok is true if the value received was delivered by a successful send operation to the channel, or false if it is a zero value generated because the channel is closed and empty.
Index expression
An index expression on a map a of type map[K]V used in an assignment or initialization of the special form
v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]
var v, ok T = a[x]
yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.
Assignments
A tuple assignment assigns the individual elements of a multi-valued operation to a list of variables. There are two forms. In the first, the right hand operand is a single multi-valued expression such as a function call, a channel or map operation, or a type assertion. The number of operands on the left hand side must match the number of values. For instance, if f is a function returning two values,
x, y = f()
assigns the first value to x and the second to y. In the second form, the number of operands on the left must equal the number of expressions on the right, each of which must be single-valued, and the nth expression on the right is assigned to the nth operand on the left.
Therefore as you can see this behavior being specified by language design and you cannot achieve those specified for Receive operator and Index expression by yourself.
You are confusing multiple values returned from a function with the so-called "comma ok" idiom.
With a function's return values you must either handle all of them, or none of them. Simple.
"Comma ok" is more subtle. Often the behavior is changed if you have a second value specified. Consider these statements:
v, ok := x.(int)
// vs
v := x.(int)
If x is an interface holding an integer, all is well, however, if x holds a different type the first statement will work (returning 0, false), and the second one will panic.
Each type of statement with a "comma ok" form is a different special case, and they are not the same as other kinds of multiple assignments (such as multiple function return values). You cannot compare them, each is its own thing, with its own rules.
Below is a piece of Go code I have question about.
Specifically, what is a in this function?
func DPrintf(format string, a ...interface{}) (n int, err error) {
if Debug > 0 {
n, err = fmt.Printf(format, a...)
}
return
}
Could anyone tell me what the three dots are here?
And what does ...interface{} do?
A parameter type prefixed with three dots (...) is called a variadic parameter. That means you can pass any number or arguments into that parameter (just like with fmt.Printf()). The function will receive the list of arguments for the parameter as a slice of the type declared for the parameter ([]interface{} in your case). The Go Specification states:
The final parameter in a function signature may have a type prefixed with .... A function with such a parameter is called variadic and may be invoked with zero or more arguments for that parameter.
A parameter:
a ...interface{}
Is, for the function equivalent to:
a []interface{}
The difference is how you pass the arguments to such a function. It is done either by giving each element of the slice separately, or as a single slice, in which case you will have to suffix the slice-value with the three dots. The following examples will result in the same call:
fmt.Println("First", "Second", "Third")
Will do the same as:
s := []interface{}{"First", "Second", "Third"}
fmt.Println(s...)
This is explained quite well in the Go Specification as well:
Given the function and calls
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
within Greeting, who will have the value nil in the first call, and []string{"Joe", "Anna", "Eileen"} in the second.
If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.
Given the slice s and call
s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)
within Greeting, who will have the same value as s with the same underlying array.
As far as the interface{} term, it is the empty interface. In other words, the interface implemented by all variables in Go.
This is sort of analogous to java.lang.Object or System.Object in C#, but is instead inclusive of every variable type in the language. So it lets you pass in anything to the method.
I want to call my function test and use one of the return values. How do I say give me the first or second value? I thought the below would give me "one" but [1] is incorrect usage causing a compile error
package main
import (
"fmt"
)
func test() (int, string) { return 1, "one"; }
func main() {
i,sz:=test()
fmt.Printf("%d=%s\n",i,sz)
fmt.Printf("%s", test()[1]) //error
}
As far as I know, you can't subscript function return values. You can do:
_, someString := test();
fmt.Println(someString);
Citing the Go Language Specification:
A primary expression of the form a[x]
denotes the element of the array, slice, string or map a indexed by x. The value x is called the index or map key, respectively. [...] Otherwise [if a is not an array, slice string or map] a[x] is illegal.
Multiple return values in Go, however, are not arrays being returned, but a separate language feature. This must be so, because an array can only hold elements of a single type, but return values can be of different types.
But since return values are not arrays (or slices, strings or maps), the a[x] syntax is, per language spec, a syntax error. As a result, as #dav has already correctly stated, you will have to actually assign the return value to a variable in order to use it elsewhere.
In special cases, you may be able to use this bit of trivia to avoid variable assignment:
As a special case, if the return values of a function or method g are equal in number and individually assignable to the parameters of another function or method f, then the call f(g(parameters_of_g)) will invoke f after binding the return values of g to the parameters of f in order.
Which makes the following possible:
func foo() (int, string) {
return 42, "test";
}
func bar(x int, s string) {
fmt.Println("Int: ", x);
fmt.Println("String: ", s);
}
func main() {
bar(foo())
}