1

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.

1 Answers1

1

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
Matt
  • 3,677
  • 1
  • 14
  • 24
  • Thank you. This already made it much clearer, but it is still muddy to me why the step after "Returning Append manipulator" is "Executing Append manipulator" and not Prepend. Haskell also returns functions, but when I do `addFoo :: String -> String` `addFoo x = x ++ "Foo"` `addBar :: String -> String` `addBar x = x ++ "Bar"` `main :: IO ()` `main = do` ` print(addFoo(addBar "start"))` it will execute from inside to outside like I expect. What am I missing? – jbauer180266 Feb 08 '19 at 05:17
  • Your Haskell functions return the string directly. The Go functions return functions that return strings. The final function returned (by `AppendDecorator`) is then executed, and in turn executes the rest. I can't remember enough Haskell to write an equivalent function, something like `appendDecorator :: (String -> String) -> String -> String` – Matt Feb 08 '19 at 05:37
  • Thanks again and sorry for being so dense, but can't I explicitly compose the functions, i.e. `print((addFoo . addBar) $ "start")`? And because of currying these should return actual functions - e.g. addFoo returns a function that takes a string and appends "Foo" to it. But even when I explicitly combine the functions, I'll still get "startBarFoo". Maybe I just need to sleep over it. [Append ++ is String -> String -> String and so pretty close to your statement.] – jbauer180266 Feb 08 '19 at 05:44