Home > Blockchain >  Go: own List type incompatible with own Functor type
Go: own List type incompatible with own Functor type

Time:07-08

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]?

CodePudding user response:

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

CodePudding user response:

The types in the function signatures must match exactly. So you simply need to change the return type of List.Map method to return a Functor[T].

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

This now compiles because List[T] does implement exactly Functor[T] with its own Map method, and both instantiations of demo and List share the same concrete type.

Fixed playground: https://go.dev/play/p/a4GeqXstjct

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

Yes it is, however Go generics don't support any form of type variance, therefore by returning List[T] as in your example the method signature differs from that of the interface.

  • Related