A very valuable feature of Go is that it is non-magic. With very few exceptions, you can look at a part of some Go code and can tell what it does, without ambiguity.
The first code snippet, however, is already quite ambiguous. You’ll need to look up the definition of Map to tell if func(i) is meant to have a return value. Otherwise the return statement inside would be an error.
After checking Map
, we know that the func must return some U
, but what concrete type will it be? For this, you’d have to analyze the function body, where the expression i*i
indicates that the return type must be i
's type.
So far so good, but what if the function body is something like,
if i < 0 {
return -1
}
return 1
This code returns numeric literals that, by definition have no particular type unless they get assigned to a variable. So you’d have to look where the function result is going to be used - this means, we need to go back to Map
. Here, the func result is assigned to res[i]
, and res
is of type []U
but what is U
?
In main
, the result of Map
is assigned to variable result
, which gets its type from the result of Map
. We are in a catch 22.
Also, what is the correct type of function parameter i
? You need to look up Map again, see that the type parameter T
is the same as the one for numbers
, then go to the definition of numbers, and then you finally know the type of i
.
This is not very readable if you ask me. Losing readability is not a small price for saving a few keystrokes.
And there is another problem. Consider that you can also pass a regular function to Map
. With the type inference you suggest, the following declaration would be valid:
func square(i) {
return i*i
}
At this point, it is impossible to tell what parameter and return types square
has. It totally depends on the context where square is used.
In other words, the above function may or may not be correct code. We simply cannot tell without looking at all of the places where square is used.
And the places where square is finally used can be quite far away, maybe even in a different file.
func main() {
numbers:=[]int64{2,1,4}
result:=Map(numbers, square)
Print(result)
}
How can the compiler, or a human reviewer, determine whether func square
is type safe? Both, compiler and human, would have to -
- dig through all code to ensure that
square
is not used anywhere else except in the Map
context that provides the actual types,
- find all
Map
calls to ensure that square
is not accidentally used with, for example, strings or structs, for which i*i
is not exactly a well-defined expression.
So things are getting super complex. The code is no more easy to review, nor would the compiler be able to compile the code fast.
I know that Go’s verbosity is often frowned upon, but looking at the alternatives, I’d rather have a verbose and boring language where I can see at the first glance what the code in front of me is actually doing.