Search code examples
listgogenericsfunctor

Go: own List type incompatible with own Functor type


In order to learn more about Go, generics, and functional programming, I implemented a List type (excerpt of list.go):

package funcprog

type Function[T any] func(T) T

type List[T any] []T

func (l List[T]) Map(f Function[T]) List[T] {
    var ys List[T]
    for _, x := range l {
        y := f(x)
        ys = append(ys, y)
    }
    return ys
}

Which works quite well (main.go):

package main

import (
    "fmt"

    "funcprog"
)

func main() {
    numbers := funcprog.List[int]{0, 1, 2, 3}
    twice := func(x int) int { return x * 2 }
    fmt.Println(numbers.Map(twice))
}
$ go run main.go
[0 2 4 6]

My List is actually a Functor, so I wrote this interface (functor.go):

package funcprog

type Functor[T any] interface {
    Map(Function[T]) Functor[T]
}

However, If I want to use my List as a Functor (main.go, modified):

import (
    "fmt"

    "funcprog"
)

func demo[T int](f funcprog.Functor[T]) {
    fmt.Println(f.Map(func(x T) T { return x * 2 }))
}

func main() {
    numbers := funcprog.List[int]{0, 1, 2, 3}
    demo[int](numbers)
}

I get this error:

funcprog.List[int] does not implement funcprog.Functor[int] (wrong type for Map method)
    have Map(f funcprog.Function[int]) funcprog.List[int]
    want Map(funcprog.Function[int]) funcprog.Functor[int]

Isn't my List[int] also a Functor[int], because List[T] satisfies Functor[T]?


Solution

  • When looking if a type implements an interface, go does not try to do "interface mapping" on the elements of the signature of that function, it only compares the exact signatures.

    If you want to have your List[T] type implement your Functor[T] interface, you should change the signature of the Map() method :

    func (l List[T]) Map(f Function[T]) Functor[T] {
        ...
    }
    

    To mention one extra point : this is not linked to generics, but to how type checking is implemented on interfaces.

    Here is another example (without generics) :

    type MyStr string
    
    // MyStr implements fmt.Stringer
    func (s MyStr) String() string {
        return string(s)
    }
    
    // but this method does not fulfill the Concatter interface below
    func (s MyStr) Concat(x string) MyStr {
        return s + " " + MyStr(x)
    }
    
    type Concatter interface {
        Concat(s string) fmt.Stringer
    }
    
    var _ Concatter = MyStr("") // compilation error
    

    https://go.dev/play/p/tKDGEXlYHyl