32

Function Currying in Go

 4 years ago
source link: https://www.tuicool.com/articles/UbeamaR
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

EnMFr2M.jpg!web

Photo by Clément H on Unsplash

Go can be used to program in a functional style, previously I’ve written about how we can use this to implement Continuation Passing Style programming. As such, it is possible to implement currying in Go as well. Before we take a look at how we can implement this in Go, let’s look practical look at what function currying actually is, and why we want this.

Simply put, when you use currying you’re writing functions that take one parameter and return a closure. But why might you want to write these types of functions? To answer this, let’s take a look at an imaginary language that looks suspiciously like Java :sweat_smile:

What you might encounter in a more traditionally object oriented language is a method like this:

public String magic(a String, b String, c Int, d Person) {
    return a + " " + b + String.parseInt(c) + d.ToString() 
}

This function takes a lot of parameters making it hard to compose the function. Hence in these types of languages you don’t see function composition happening all that often. Instead you end up with rather large methods that in the best case aren’t reusable but don’t violate the Single-Responsibility Principle, and in the worst case encapsulate too much functionality and are a nightmare to maintain.

Our goal should be to have functions that do one thing, and do one thing well. By removing the chain of arguments we can split up our ‘monolith function’ into smaller and composable parts. Unfortunately not all languages allow for this to happen using function currying, as you really need first-class functions to do this.

A Go Example

Let’s set the stage with a simple Go program and then slowly improve. Our users come to us and tell us they need to have a program that allows them to greet their clients. One implementation would be to go the traditional route.

func greet(greeting, name string) string {
    return fmt.Sprintf("%v %v", greeting, name)
}func main() {
    fmt.Println(greet("Good Morning", "Sam")
}

This works and our clients love it! They love it so much that we end up having to insert greetings in all sorts of places. Though we notice we only greet for mornings and afternoons so always having to pass “Good morning” or “Good afternoon” becomes a bit annoying. It would be great if we could create an instance of our “greet” function with the first argument pinned down. One option here is partial application and another is function currying. Well, this is a post about currying so let’s pretend partial application doesn’t exist. :no_mouth:

func prefixedGreet (p string) func(string) string {
   return func(n string) string {
        return greet(p,n)
   }
} 

We have essentially wrapped our “Greet” function into another function taking one argument and returning a closure .

The ‘straightforward’ way of calling this would result in chains like this: prefixedGreet("Good morning")("John") but that would not really help us all that much. Though, functions are first-class citizens. We can store them like data and use them later, hence the solution would look more like this.

var (
    eveningFn = prefixedGreet("Good evening")
    morningFn = prefixedGreet("Good morning")
)func main() {
    fmt.Println(eveningFn("Ricardo")
    fmt.Println(morningFn("Isabel")
)

In this way you can break up longer functions as well, each into respective smaller functions that just keep applying further certain parts. With more functions we could start mixing and matching them. Below I’ll post some snippets with a scenario where you can see the functions being combined.

Multiplying and subtraction

As another example, it turns out our users are really into subtractions and multiplications so they ask us to provide these functions. They also tend to perform both on the same data, but in different order. Because.. yeah.. they like that.

With currying we could write these functions as follows:

func multiply(a int) func(int) int {
   return func(i int) int {
       return a * i
   }
}func subtract(a int) func(int) int {
    return func(i int) {
        return i - a
    }
}

With function currying, we could create new (local) functions to have both orders of operation present.

func main() {
    in := userInput() // imagine this gives us something
    m := multiply(4)  // example data
    s := subtract(10) // example data
    // subtract then multiply
    sm := func(i int) int { return m(s(i)) }
    ms := func(i int) int { return s(m(i)) }
    fmt.Printf("%v %v\n", ms(in), sm(in))
}

Here we have our multiply and subtract functions defined, but our user input is fetched via a function somewhere. By using currying we managed to create a function sm which first applies the subtraction and then the multiplication, and an ms for the reverse order. (Notice how inside the sm function our call looks like m(s(i)) . Remember Go applies the inner function first. :wink:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK