如何在 Go 中模拟 `fmap`?

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 实施。对此有通用的解决方法吗?

Go 的泛型和方法的组合不像 Haskell 的类型类那样富有表现力;至少还没有。特别是,正如 kostix 在

中指出的那样

Go permits a generic type to have methods, but, other than the receiver, the arguments to those methods cannot use parameterized types.

(source)

由于 Go 方法不能引入新的类型参数,因此在 fmap 方法中访问 B 的唯一方法是在 Functor 类型的声明中引入它,和你一样。但这没有意义,因为根据类别理论,functor 接受一个类型参数,而不是两个。

这个例子可能足以让您相信,在 Go 中使用泛型和方法来模拟 Haskell 类型类是徒劳的。


不过,您可以做的一件事是实现 fmap,不是作为方法,而是作为 top-level 函数:

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)

但仅仅因为你可以并不意味着你应该。总是问问自己,将某种其他语言的方法转换为 Go 感觉 是否正确。

我要补充一点,您遇到的一些问题是您开始时定义不正确。提议的 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 ⚠️
}

解决你上面的问题,这就是我们喜欢写的-

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 在链接的答案中指出的那样,methods may not take additional type arguments