使用 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) -> .....
任何 a
和 b
.
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
混淆)
在 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) -> .....
任何 a
和 b
.
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
混淆)