Go:自己的List类型与自己的Functor类型不兼容

huangapple go评论66阅读模式
英文:

Go: own List type incompatible with own Functor type

问题

为了更好地了解Go语言、泛型和函数式编程,我实现了一个List类型(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
}

这段代码运行得很好(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]

我的List实际上是一个Functor,所以我写了这个接口(functor.go):

package funcprog

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

然而,如果我想将我的List用作Functor(修改后的main.go):

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)
}

我会得到这个错误:

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]

难道我的List[int]不是一个Functor[int]吗?因为List[T]满足Functor[T]的要求。

英文:

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

答案1

得分: 1

在函数签名中,类型必须完全匹配。所以你只需要改变List.Map方法的返回类型为Functor[T]

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

现在这个代码可以编译通过,因为List[T]确实实现了自己的Map方法,与demoList的实例共享相同的具体类型。

修复后的代码示例:https://go.dev/play/p/a4GeqXstjct

我的List[int]不也是Functor[int]吗?因为List[T]满足Functor[T]

是的,它是的。然而,Go泛型不支持任何形式的类型变异,因此通过在你的示例中返回List[T],方法的签名与接口的签名不同。

英文:

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.

答案2

得分: 1

当检查一个类型是否实现了一个接口时,Go语言不会尝试对该函数的签名元素进行"接口映射",它只会比较确切的签名。

如果你想让你的List[T]类型实现你的Functor[T]接口,你应该改变Map()方法的签名:

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

还有一点需要提到:这与泛型无关,而是与接口上的类型检查实现方式有关。

这里有另一个例子(没有泛型):

type MyStr string

// MyStr实现了fmt.Stringer接口
func (s MyStr) String() string {
	return string(s)
}

// 但是这个方法不满足下面的Concatter接口
func (s MyStr) Concat(x string) MyStr {
	return s + " " + MyStr(x)
}

type Concatter interface {
	Concat(s string) fmt.Stringer
}

var _ Concatter = MyStr("") // 编译错误

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

英文:

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

huangapple
  • 本文由 发表于 2022年7月8日 20:11:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/72911207.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定