Haskell 绑定和连接之间的关系是什么?
我得到的印象是,Haskell 绑定和连接之间的关系是什么?,haskell,monads,category-theory,Haskell,Monads,Category Theory,我得到的印象是,(>>=)(Haskell使用)和join(数学家首选)是“相等的”,因为一个人可以根据另一个人来写一个: import Control.Monad (join) join x = x >>= id x >>= f = join (fmap f x) 此外,由于bind可用于替换fmap: fmap f x = x >>= (return . f) 我有以下问题: 对于join,是否有fmap的(非递归)定义?(fmap fx=join$
(>>=)
(Haskell使用)和join
(数学家首选)是“相等的”,因为一个人可以根据另一个人来写一个:
import Control.Monad (join)
join x = x >>= id
x >>= f = join (fmap f x)
此外,由于bind
可用于替换fmap
:
fmap f x = x >>= (return . f)
我有以下问题:
join
,是否有fmap
的(非递归)定义?(fmap fx=join$fmap(return.f)x
遵循上述等式,但是递归的。)bind
(在单子的定义中)时的结论,还是使用join
时的假设bind
比join
更“强大”吗?“更强大”意味着什么return::a->ma
bind::ma->(a->mb)->mb
return::a->ma
fmap::(a->b)->ma->mb
join::m(ma)->ma
join
定义fmap
,因为否则我们可以从上面的第二个列表中删除fmap
bind
还是用join
和fmap
来定义你的单子。如果你看到第二个定义,你会更容易理解这句话,但仅此而已bind
比join
更“强大”。它与join
和fmap
的组合完全一样“强大”,如果“强大”的意思是它有能力定义monad(总是与return
结合使用)bind
允许您将策略/计划/计算(在上下文中)组合或链接在一起。例如,让我们使用Maybe
上下文(或Maybe
monad):
fmap
还可以让您将上下文中的计算链接在一起,但代价是每一步都增加嵌套。[1]
λ: fmap plusOne (Just 3)
Just (Just 4)
这就是为什么您需要join
:将两个嵌套级别压缩为一个。记住:
join :: m (m a) -> m a
只有挤压步骤并不能让你走得很远。您还需要fmap
来拥有monad–和return
,在上面的示例中,这是只是
[1] :fmap
和(>>=)
不要将它们的两个参数按相同的顺序排列,但不要让它们混淆了你的意思
在join
方面是否有fmap
的[定义]
不,没有。这可以通过尝试来证明。假设给我们一个任意类型的构造函数T
,函数:
returnT :: a -> T a
joinT :: T (T a) -> T a
仅从这些数据,我们就要定义:
fmapT :: (a -> b) -> T a -> T b
让我们来画一个草图:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = tb
where
tb = undefined -- tb :: T b
我们需要以某种方式获得类型为tb
的值<代码>ta::ta本身无法实现,因此我们需要生成TB
值的函数。仅有的两个候选者是joinT
和returnT
<代码>关节没有帮助:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = joinT ttb
where
ttb = undefined -- ttb :: T (T b)
它只是把问题解决了,因为在这种情况下需要一个T(tb)
值并没有什么改善
我们可以尝试使用returnT
:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT b
where
b = undefined -- b :: b
现在我们需要一个b
值。唯一能给我们的是f
:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT (f a)
where
a = undefined -- a :: a
现在我们陷入困境:没有什么能给我们一个a
。我们已经用尽了所有可能的方法,因此不能用这样的术语定义fmapT
题外话:使用这样的函数作弊是不够的:
extractT :: T a -> a
使用extract
,我们可以尝试a=extract ta
,从而导致:
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT (f (extractT ta))
然而,fmapT
只有正确的类型是不够的:它还必须遵循函子定律。特别是,fmapT id=id
应保持不变。根据此定义,fmapT id
是returnT。extract
,一般来说,它不是id
(作为Monad
和Comonad
实例的大多数函子都作为示例)
“每个单子都是函子”是使用
bind
(在单子的定义中)时的结论,还是使用join
时的假设
“每个单子都是函子”是一个假设,或者更准确地说,是单子定义的一部分。选择一个任意的例子,这里是艾米丽·里尔,p。154:
定义5.1.1。类别C上的单子包含
- 内函子T:C→ C
- a单位自然变换η:1C⇒ T、 及
- a乘法自然变换μ:T2⇒ T,
Monad
的Haskell类型构造函数T
,该内函子的对象映射是T
本身,态射映射是其fmap
。T
将是一个函子
实例,因此将有一个fmap
,在当代Haskell中,由Applicative
(扩展为函子
)作为Monad
的超类来保证
但这就是故事的全部吗?就哈斯克尔而言。我们知道它是存在的,而且在不久的过去,函子不是单子
的超类。这两个事实仅仅是哈斯卡尔主义吗?不完全是。在这篇经典论文中,尤金尼奥·莫吉揭示了以下定义(第3页):
定义1.2([Man76])类别上的Kleisli三元组
fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT (f (extractT ta))
class KleisliTriple t where
return :: a -> t a
(=<<) :: (a -> t b) -> t a -> t b
-- (return =<<) = id
-- (f =<<) . return = f
-- (g =<<) . (f =<<) = ((g =<<) . f =<<)