I was reading this question: Decorator functions in Go and am wondering why the execution order of the example in the accepted answer seems reversed to me.
I have broken it down to the following minimal example and am wondering if the effect is due to function chaining.
// Interesting Part
some_string := "Some_String"
var fn3 StringManipulator = ident
fn3 = AppendDecorator(" GOLANG", ToLower(PrependDecorator("DECORATED ", fn3)))
fmt.Println(fn3(some_string))
// Prints "DECORATED some_string golang"
// Function Definitions
func ToLower(m StringManipulator) StringManipulator {
return func(s string) string {
lower := strings.ToLower(s)
return m(lower)
}
}
func AppendDecorator(x string, m StringManipulator) StringManipulator {
return func(s string) string {
return m(s + x)
}
}
func PrependDecorator(x string, m StringManipulator) StringManipulator {
return func(s string) string {
return m(x + s)
}
}
As mentioned in the code this yields "DECORATED some_string golang" indicating that the functions were executed from left to right, whereas normal functions evaluate from innermost to outermost, i.e. right to left. [This reminds me of postmultiplication of transformation matrices - there the order is also "reversed", i.e. M_1 * M_2 * M_3] Is this due to function chaining or what is the reason? Could somebody help me understand how this executes in detail?
Thank you in advance.
I rewrote your example to illustrate.
The nested function calls execute from inside to outside. Each function call returns a function. Eventually the variable m
is assigned the result of AppendDecorator
which is itself a function composed of all the decorators that looks something like this:
m := func(s string) string {
return ("DECORATED " + strings.ToLower(s + " GOLANG"))
}
When we execute m(s)
(inside fmt.Println(m(s)
) we are executing the function returned by AppendDecorator
. This function calls m(s + x)
where m
is the function returned by ToLower
. When this function executes it calls m(lower)
where m
is the function returned by PrependDecorator
. When this function executes it calls m(x + s)
where m
is the Identity function that we passed in.
package main
import (
"fmt"
"strings"
)
// StringManipulator manipulate a string
type StringManipulator func(str string) string
// Identity a string manipulator that leaves the string unchanged
func Identity(s string) string {
fmt.Println("Executing Identity manipulator")
return s
}
// ToLower a lower case string manipulator
func ToLower(m StringManipulator) StringManipulator {
fmt.Println("Returning ToLower manipulator")
return func(s string) string {
fmt.Println("Executing ToLower manipulator")
lower := strings.ToLower(s)
return m(lower)
}
}
// AppendDecorator append a string manipulator
func AppendDecorator(x string, m StringManipulator) StringManipulator {
fmt.Println("Returning Append manipulator")
return func(s string) string {
fmt.Println("Executing Append manipulator")
return m(s + x)
}
}
// PrependDecorator prepend a string manipulator
func PrependDecorator(x string, m StringManipulator) StringManipulator {
fmt.Println("Returning Prepend manipulator")
return func(s string) string {
fmt.Println("Executing Prepend manipulator")
return m(x + s)
}
}
func main() {
s := "Some_String"
m := AppendDecorator(" GOLANG", ToLower(PrependDecorator("DECORATED ", Identity)))
fmt.Println(m(s))
}
The output from m := AppendDecorator(" GOLANG", ToLower(PrependDecorator("DECORATED ", Identity)))
is:
Returning Prepend manipulator
Returning ToLower manipulator
Returning Append manipulator
and the output from fmt.Println(m(s))
is:
Executing Append manipulator
Executing ToLower manipulator
Executing Prepend manipulator
Executing Identity manipulator
DECORATED some_string golang