使用 id 和 return 定义 fmap

Defining fmap with id and return

在 Bartosz Milewski 的 程序员范畴论 中,Milewski 编写了以下代码来定义 return 和 'fish' 运算符(Kleisli 范畴中的组合)对于 Writer monad。

return :: a -> Writer a
return x = (x, "")

(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)
m1 >=> m2 = \x ->
  let (y, s1) = m1 x
      (z, s2) = m2 y
  in  (z, s1 ++ s2)

然后他继续定义 fmap 如下:

fmap f = id >=> (\x -> return (f x))

我很难理解这里如何使用 id 函数。 fish 运算符的第一个参数显然是 (a -> Writer b) 但 id 具有类型签名 a -> a

这是我理解上的错误还是缺陷?用 return 替换 id 对我来说更有意义。

不要忘记通用量化。

(>=>) 的类型为 (a -> Writer b) -> ..... 任何 ab.

id 具有类型 a -> a 对于任何 a.

因此,特别是对于任何 b,fish 也具有类型 (Writer b -> Writer b) -> ...(仅将 a = Writer b 作为特例)。

此外,id 还键入了 Writer b -> Writer b(同样,作为一种特殊情况)。

这里的"trick"是对"merge"两种类型使用统一。 我们从要求 (a -> Writer b) = (a' -> a') 开始,然后我们推断 a = a'Writer b = a'。从这里可以看出,这两种类型是可以统​​一的,所以传参不矛盾

(另外注意这里我们把id类型里的a重命名为a',避免和鱼的另一个a混淆)