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