Dynamically scoped
What if we accidentally initialized sum in an outer scope from where we defined our function? Notice that there is no sum variable initialization in the same scope as our anonymous function:
func addTwoDynamic() func() int {
return func() int {
sum += 2
return sum
}
}
When we run this in our main() function:
twoMoreDynamic := addTwoDynamic()
fmt.Println(twoMoreDynamic())
fmt.Println(twoMoreDynamic())
Our Go runtime looks in the environment in which the anonymous function was called, rather than where it was defined (as is the case in lexical scoping). If addTwoDynamic had been nested several stack frames deep, our Go runtime would look where addTwoDynamic was defined for sum. If it was not found there, it would continue up the stack until sum is found. So, we see that dynamic scoping adds complexity and might cause the value of sum to change in unpredictable ways, or at least in ways that are more difficult to debug.
The following is the output:
7
9
What happened? Since sum was not defined in the scope in which our anonymous function was defined, Go found it in the global scope. It's value was 5. addTwoDynamic added 2 to 5 and got 7. addTwoDynamic did it again and got 9. Probably not what we wanted.
Being able to pass around lexical context is powerful and guarantees that we won't have side effects that might occur with dynamic scoping. We'll look at a practical example where we create an application context, for example, database connection, logger, and so on, at application startup and pass that context around where needed throughout our application.