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.