Home > other >  Are function declarations and function expressions implemented differently in Go? If yes, why?
Are function declarations and function expressions implemented differently in Go? If yes, why?

Time:09-03

I just got into programming again with Go (with no experience whatsoever in low-level languages), and I noticed that function expressions are not treated the same as function declarations (go1.18.5 linux/amd64).

For instance, this works (obviously):

package main

import "fmt"

func main() {
  fmt.Println("Do stuff")
}

But this outputs an error:

package main

import "fmt"

var main = func() {
  fmt.Println("Do stuff")
}
./prog.go:3:8: imported and not used: "fmt"
./prog.go:5:5: cannot declare main - must be func

Go build failed.

Even specifying the type as in var main func() = func() {} does nothing for the end result. Go seems to, first of all, evaluate if the main identifier is being used in any declaration, ignoring the type. Meanwhile, Javascript folks seem to choose what is more readable, like there's no underlying difference between them.

Questions:

  • Are function declarations and function expressions implemented differently under the hood, or is this behavior just hard-coded?
  • If yes, is the difference between these implementations critical?
  • Is Go somewhat better in any way for doing it the way it does?

CodePudding user response:

From the spec:

The main package must [...] declare a function main that takes no arguments and returns no value.

The moment you write var main you prevent the above requirement from being met, because what you are creating is a variable storing a reference to a function literal as opposed to being a function declaration.

A function declaration binds an identifier, the function name, to a function.

A function literal represents an anonymous function.

So:

Are function declarations and function expressions implemented differently under the hood?

Yes.

Is Go somewhat better in any way for doing it the way it does?

Generally, no. It comes with the territory of being a typed language, which has all sorts of pros and cons depending on use.

If yes, is the difference between these implementations critical?

By what standard? As an example, a variable referencing a function literal could be nil, which isn't usually something you'd like to call. The same cannot happen with function declarations (assuming package unsafe is not used).

CodePudding user response:

The code

func main() {
  fmt.Println("Do stuff")
}

binds a function to the identifier main.

The code

var main = func() {
  fmt.Println("Do stuff")
}

binds a variable of type func () to the identifier main and initializes the variable to the result of a function expression.

Are function declarations and function expressions implemented differently under the hood, or is this behavior just hard-coded?

A function expression evaluates to a function value. A function declaration binds a function value to a name. There is no difference in the implementation of these function values (but the result of an expressions can additionally close over function scoped variables).

If yes, is the difference between these implementations critical?

Yes.

  • The question points out one scenario where the difference is important (program execution starts at the function main in package main)
  • The compiler cannot inline a function called through a variable.
  • Functions cannot be declared inside another function, but a function expression can be assigned to a variable in a function.

Is Go somewhat better in any way for doing it the way it does?

Other languages make a distinction between an identifier bound a function and and identifier bound to a variable with a function type. Go is no better or worse in than those languages with regards to the binding of identifiers.

The OP says in a comment:

The question was about the difference between javascript and go, though, but you also answered it with "It comes with the territory of being a typed language".

The difference is unrelated to being a typed language. Identifiers can be bound to constants, types, variables and functions in Go. I may be going out on a limb here, but non-reserved identifiers in Javascript are always bound to variables.

  • Related