英文:
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
方法,与demo
和List
的实例共享相同的具体类型。
修复后的代码示例: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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论