英文:
How can I emulate `fmap` in Go?
问题
我想在Go语言中模拟fmap
函数。这是一个简单的例子:
type S [A any] struct {
contents A
}
type Functor [A any, B any] interface{
fmap(f func(A)B) B
}
func (x S[A]) fmap (f func(A)B) S[B] {
x.contents = f(x.contents)
return x
}
这段代码出现了错误:undefined: B
,涉及到interface
的实现。是否有常见的解决方法?
英文:
I would like to emulate fmap
in Go. A trivial example:
type S [A any] struct {
contents A
}
type Functor [A any, B any] interface{
fmap(f func(A)B) B
}
func (x S[A]) fmap (f func(A)B) S[B] {
x.contents = f(x.contents)
return x
}
This fails with: undefined: B
with regards to the interface
implementation. Is there a common workaround for this?
答案1
得分: 2
Go的泛型和方法的组合并不像Haskell的类型类那样表达力强大,至少目前还不是。正如kostix在他的评论中指出的那样:
Go允许泛型类型拥有方法,但是除了接收器之外,这些方法的参数不能使用参数化类型。
(来源:https://go.dev/doc/faq#generic_methods)
由于Go方法不能引入新的类型参数,在你的fmap
方法中访问B
的唯一方法是在Functor
类型的声明中引入它,就像你所做的那样。但是这是没有意义的,因为根据范畴论,函子只接受一个类型参数,而不是两个。
这个例子足以让你相信,在Go中使用泛型和方法来模拟Haskell类型类是徒劳无功的。
不过,你可以将fmap
实现为一个顶层函数,而不是一个方法:
package main
import "fmt"
type S[A any] struct {
contents A
}
func Fmap[A, B any](sa S[A], f func(A) B) S[B] {
return S[B]{contents: f(sa.contents)}
}
func main() {
ss := S[string]{"foo"}
f := func(s string) int { return len(s) }
fmt.Println(Fmap(ss, f)) // {3}
}
(Playground:https://go.dev/play/p/BHqEDBnoQsI)
但是,仅仅因为你可以这样做,并不意味着你应该这样做。始终要问自己,将某种方法从其他语言转移到Go中是否“感觉”正确。
英文:
The combination of Go's generics and methods isn't as expressive as Haskell's typeclasses are; not yet, at least. In particular, as pointed out by kostix in his comment,
> Go permits a generic type to have methods, but, other than the receiver, the arguments to those methods cannot use parameterized types.
(source)
Since Go methods cannot introduce new type parameters, the only way to have access to B
in your fmap
method is to introduce it in the declaration of your Functor
type, as you did. But that doesn't make sense because, according to category theory, a functor takes one type parameter, not two.
This example may be enough to convince you that using generics and methods to emulate Haskell typeclasses in Go is a fool's errand.
One thing you can do, though, is implement fmap
, not as a method, but as a top-level function:
package main
import "fmt"
type S[A any] struct {
contents A
}
func Fmap[A, B any](sa S[A], f func(A) B) S[B] {
return S[B]{contents: f(sa.contents)}
}
func main() {
ss := S[string]{"foo"}
f := func(s string) int { return len(s) }
fmt.Println(Fmap(ss, f)) // {3}
}
But just because you can doesn't mean that you should. Always ask yourself whether transposing an approach from some other language to Go feels right.
答案2
得分: 0
我会为你翻译以下内容:
我想补充一点,你遇到的问题之一是你从一个错误的定义开始。在提议的Functor
中应该有一些立即引起警觉的问题 -
type Functor [A any, B any] interface{
// ^ Functor 应该包装一个单一类型 ⚠️
fmap(f func(A)B) B
// ^ 应该返回包装了 B 的 Functor ⚠️
}
修复你上面的问题,这是我们想要写的内容 -
type Functor[A any] interface{
fmap[B any](f func(A)B) Functor[B]
}
然而,Go 给出了警告,直接反馈了你所面临的问题 -
interface method must have no type parameters
undefined: B
正如 @jub0bs 在链接的答案中指出的那样,方法不能带有额外的类型参数。
英文:
I would add that some of the issue you are having is that you started with an incorrect definition. There should be some immediate red flags in the proposed Functor
-
type Functor [A any, B any] interface{
// ^ Functor should wrap a single type ⚠️
fmap(f func(A)B) B
// ^ should return Functor-wrapped B ⚠️
}
Fixing the issue you have above, this is what we'd like to write -
type Functor[A any] interface{
fmap[B any](f func(A)B) Functor[B]
}
However Go warns us giving us direct feedback on the issue you are facing -
interface method must have no type parameters
undefined: B
As @jub0bs points out in the linked answer, methods may not take additional type arguments.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论