DEV Community

Cover image for Go's method is curried funtion
Yasuhiro Matsumoto
Yasuhiro Matsumoto

Posted on

Go's method is curried funtion

#go

Go's method is not same in other languages. In C++, this should not be null.

class Foo {
  public:
    void doSomething() {}
};

int
main() {
  Foo *foo = nullptr;
  foo->doSomething();
}
Enter fullscreen mode Exit fullscreen mode

Go's method does not require that the receiver is instanciated.

package main

import (
    "fmt"
)

type Foo struct {
}

func (f *Foo) doSomething() {
    fmt.Println(f)
}

func main() {
    var foo *Foo
    foo.doSomething()
}
Enter fullscreen mode Exit fullscreen mode

And the method hold receiver value. This is called Method Values.

package main

import (
    "fmt"
)

type Foo struct {
    Value string
}

func (f *Foo) WhatsUp() {
    fmt.Println(f.Value)
}

func doSomething(f func()) {
    f()
}

func main() {
    foo := &Foo{Value: "Hello, World"}
    doSomething(foo.WhatsUp)
}
Enter fullscreen mode Exit fullscreen mode

The WhatsUp is evaulated with the receiver.

package main

import (
    "fmt"
)

type Foo int

func (f Foo) doSomething() {
    fmt.Println(f)
}

func main() {
    Foo.doSomething(123)
}
Enter fullscreen mode Exit fullscreen mode

So this code is valid. Foo.doSomething is the function object. It can be called with let the function object to a variable. The function type should take an first argument which is type of the receiver.

package main

import (
    "fmt"
)

type Foo int

func (f Foo) doSomething() {
    fmt.Println(f)
}

func main() {
    var fn func(Foo)

    fn = Foo.doSomething
    fn(123)
}
Enter fullscreen mode Exit fullscreen mode

This idiom is useful to call runtime.SetFinalizer to Close something resource must be closed.

package main

import (
    "log"
    "os"
    "runtime"
    "myresource"
)

func main() {
    f := myresource.OpenSomethingMustBeClosed()
    if err != nil {
        log.Fatal(err)
    }
    runtime.SetFinalizer(f, (*myresource.Writer).Close)
}
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
nekketsuuu profile image
Takuma Ishikawa

Interesting! However, what do you mean by the words "curried function" in the title? I think Foo.doSomething is not curried. For example, if doSomething takes one argument, Foo.doSomething(123) causes an error.

func (f Foo) doSomething(b bool) {
    fmt.Println(f, b)
}

func main() {
    Foo.doSomething(123)  // error: not enough arguments
}

Maybe you want to say that Go can pass a receiver to a method value as an argument?

Collapse
 
mattn profile image
Yasuhiro Matsumoto • Edited

Yes, in strictly, as you think, this is not currying. The currrying is a transform taking one less argument.

package main

import (
    "fmt"
)

func add(n int) func(int) int {
    return func(v int) int {
        return n + v
    }
}

func main() {
    add10 := add(10)
    fmt.Println(add10(5))
}

Just metaphor :)

If this function object for currying, you can try this.

package main

import (
    "fmt"
)

type number int

func (f number) add(b number) number {
    return f + b
}

func main() {
    fmt.Println(number(3).add(4))
    fmt.Println(number.add(3, 4))
}
Collapse
 
plutov profile image
Alex Pliutau

I never saw Go people use Foo.doSomething(123) ... Can you point to some open source projects doing this?

Collapse
 
mattn profile image
Yasuhiro Matsumoto

Yes, most of cases, the function will not be called directly. As I wrote in above, it is often used as function object. See os/exec.go in Go repository.

runtime.SetFinalizer(p, (*Process).Release)