Follow

# Go4Fun - Fun Interview Puzzles

Follow # Currying in Go

go4fun.fun
·Nov 9, 2022·

Currying is yet another concept often associated with functional programming. It's close to Partial application which we discussed earlier, but not the same.

Currying is a technique of converting a function that takes multiple arguments into a chain of functions that each takes a single argument.

Currying usage could look like this in Go:

``````f := func(a int, b bool, c float64) string {
return fmt.Sprint(a) + " " + fmt.Sprint(b) + " " + fmt.Sprint(c)
}

curriedF := Curry3(f)
r := curriedF(1)(true)(5.5)

fmt.Println(r)
// Output: 1 true 5.5
``````

`Curry3` is a helper function provided by Go4Fun library that takes a 3-argument function as a parameter and does Currying: it converts a provided function of 3 arguments into a chain of 3 functions each taking exactly 1 argument.

How it can be useful?

Imagine we have `Map` function defined for slices that expects a 1-argument function `f` as a parameter. `Map` function applies function `f` to each element of the slice and returns a new slice as a result. It could be defined like this (`Seq` type is a type based on regular Go slices `type Seq[A any] []A` with additional methods defined):

``````func (seq Seq[A]) Map(f func(A) A) Seq[A] {
r := EmptySeq[A](seq.Length())
for _, e := range seq {
r = r.Append(f(e))
}
return r
}
``````

And then we define a simple function `Add` to add 2 numbers:

``````func Add(a int, b int) int {
return a + b
}
``````

And now we want to use `Map` operation to transform each element of the slice using `Add` function (add number 10 to each element of the slice):

``````seq := Seq[int]{1, 2, 3, 4, 5, 6, 7}

//Would not work! Map expects a function of 1 argument but Add has 2...
``````

To help with that we can use currying:

``````r := seq.Map(Curry2(Add)(10))

fmt.Println(r)
//Output: [11 12 13 14 15 16 17]
``````

`Curry2` converted the provided function of 2 arguments (`Add`) into a chain of 2 functions each having exactly 1 argument. And then we provided `10` value for the first 1-argument function in that chain, which returned us a remaining 1-argument function expecting the second operand for `Add` operation. This returned function had only 1 argument, thus it was compatible with `Map` function.

How `Curry2` function is implemented under the hood? In fact, the implementation is very straightforward:

``````func Curry2[A, B, C any](f func(A, B) C) func(A) func(B) C {
return func(a A) func(B) C {
return func(b B) C {
return f(a, b)
}
}
}
``````

`Curry3`... functions could be implemented similarly for currying functions of 3 or more arguments.

### UnCurrying

Since we are able to Curry something we should be able to UnCurry it back via UnCurrying.

UnCurrying is an operation opposite to Currying. It takes a chain of 1-argument functions and coverts it back to 1 function taking multiple arguments.

Usage could look like this:

``````f := func(a int) func(bool) func(float64) string {
return func(b bool) func(float64) string {
return func(c float64) string {
return fmt.Sprint(a) + " " + fmt.Sprint(b) + " " + fmt.Sprint(c)
}
}
}

unCurriedF := UnCurry3(f)
r := unCurriedF(1, true, 5.5)

fmt.Println(r)
// Output: 1 true 5.5
``````

`UnCurry3` function takes a chain of 3 1-argument functions and converts it to 1 function of 3 arguments. It's implemented trivially under the hood:

``````func UnCurry3[A, B, C, D any](f func(A) func(B) func(C) D) func(A, B, C) D {
return func(a A, b B, c C) D {
return f(a)(b)(c)
}
}
``````

Other `UnCurry` functions could be implemented similarly for uncurrying function chains with a different number of functions.

### Conclusion

In this article we looked into the concept called Currying (and the opposite concept UnCurrying). It's primarily used in functional-first languages but even in more imperative languages like Go it could sometimes be of service. If you are interested to explore more practical functional programming concepts applicable in Go and/or would like to get hands-on, welcome to join me on GitHub where I develop Go4Fun library for Go: github.com/ialekseev/go4fun. Thanks for reading!