这两个表达方式
y >> pure x
liftM (const x) y
在Haskell中具有相同的类型签名。我很好奇它们是否相同,但我既不能证明事实,也不能反对它。
如果我们重写两个表达式以便我们可以消除x
和y
那么问题就变成了以下两个函数是否相同
flip (>>) . pure
liftM . const
请注意,这两个函数都具有Monad m => a -> m b -> m a
类型。
我使用Haskell为monad,applicatives和functor提供的法则将两个语句转换为各种等价形式,但我无法在两者之间产生一系列等价。
例如,我发现y >> pure x
可以改写如下
y >>= const (pure x)
y *> pure x
(id <$ y) <*> pure x
fmap (const id) y <*> pure x
和liftM (const x) y
可以改写如下
fmap (const x) y
pure (const x) <*> y
这些对我来说都不一定是等同的,但我想不出任何不等同的情况。
另一个答案最终到达那里,但需要一条冗长的路线。所有实际需要的是liftM
,const
和单一monad定律的定义:m1 >> m2
和m1 >>= \_ -> m2
必须在语义上相同。 (实际上,这是(>>)
的默认实现,并且很少覆盖它。)然后:
liftM (const x) y
= { definition of liftM* }
y >>= \z -> pure (const x z)
= { definition of const }
y >>= \z -> pure x
= { monad law }
y >> pure x
*好的,好吧,所以liftM
的实际定义使用return
而不是pure
。随你。
让我们从flip (>>) . pure
开始,这是你提供的x >> pure y
的免费版本:
flip (>>) . pure
这是flip (>>)
只是(=<<) . const
的情况所以我们可以改写为:
((=<<) . const) . pure
由于函数组合((.)
)是关联的,我们可以将其写成:
(=<<) . (const . pure)
现在我们想重写const . pure
。我们可以注意到const
只是pure
上的(a ->)
,这意味着因为pure . pure
是fmap pure . pure
,所以const . pure
是(.) pure . const
,((.)
是fmap
的(a ->)
)。
(=<<) . ((.) pure . const)
现在我们再次联系:
((=<<) . (.) pure) . const
((=<<) . (.) pure)
是liftM
1的定义,所以我们可以替换:
liftM . const
这就是目标。两者是一样的。
1:liftM
的定义是liftM f m1 = do { x1 <- m1; return (f x1) }
,我们可以将do
变成liftM f m1 = m1 >>= return . f
。我们可以翻转(>>=)
为liftM f m1 = return . f =<< m1
和elide the m1
让liftM f = (return . f =<<)
有点无点魔术我们得到liftM = (=<<) . (.) return
另一种可能的途径,利用适用法律:
例如,我发现
y >> pure x
可以改写如下[...]fmap (const id) y <*> pure x
这相当于......
fmap (const id) y <*> pure x
pure ($ x) <*> fmap (const id) y -- interchange law of applicatives
fmap ($ x) (fmap (const id) y) -- fmap in terms of <*>
fmap (($ x) . const id) y -- composition law of functors
fmap (const x) y
......正如你所说,它与liftM (const x) y
相同。
这条路线只需要适用法律,而不是monad那些反映了(*>)
((>>)
的另一个名字)是Applicative
方法。