How can I emulate `fmap` in Go?

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

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

(Playground)

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.

huangapple
  • 本文由 发表于 2022年5月31日 00:40:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/72437559.html
匿名

发表评论

匿名网友

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

确定