Declaration binds identifier to value like package, variable, type etc. After declaration it’s important to know where in the source code identifier refers to specified value (colloquially speaking where such name can be used).

Go is lexically scoped so identifier resolution depends on where in the source code name is declared. It’s completely different approach than in dynamically scoped languages where visibility isn’t related to the place of declaration. Consider Bash script below:

#!/bin/bash f() {

local v=1

g

} g() {

echo "g sees v as $v"

} h() {

echo "h sees v as $v"

} f

g

h

Variable v is defined in function f but because g is called by f it has access to it:

> ./scope.sh

g sees v as 1

g sees v as

h sees v as

When g is called independently or different function like h is used then variable v is not defined. Visibility in dynamically scoped languages isn’t a static thing (lexical scoping is also called static scoping) but depends on the control flow.

Attempt to compile similar code in Go throws a compilation error:

package main import “fmt” func f() {

v := 1

g()

} func g() {

fmt.Println(v) // "undefined: v"

} func main() {

f()

}

Lexical scoping in Go uses blocks so it’s important to understand first what block is before trying to understand visibility rules.