LiftM可以与liftA不同吗?
-
06-07-2019 - |
题
根据 Typeclassopedia (以及其他来源), Applicative
逻辑上属于类型类层次结构中的Monad
和Pointed
(以及Functor
),所以如果今天编写Haskell前奏,我们理想情况下会有类似的东西:
class Functor f where
fmap :: (a -> b) -> f a -> f b
class Functor f => Pointed f where
pure :: a -> f a
class Pointed f => Applicative f where
(<*>) :: f (a -> b) -> f a -> f b
class Applicative m => Monad m where
-- either the traditional bind operation
(>>=) :: (m a) -> (a -> m b) -> m b
-- or the join operation, which together with fmap is enough
join :: m (m a) -> m a
-- or both with mutual default definitions
f >>= x = join ((fmap f) x)
join x = x >>= id
-- with return replaced by the inherited pure
-- ignoring fail for the purposes of discussion
(我在维基百科的解释中重新输入了这些默认定义,错误是我自己的,但如果有错误,至少原则上是可能的。)
由于目前已定义了库,我们有:
liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM :: (Monad m) => (a -> b) -> m a -> m b
和
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap :: (Monad m) => m (a -> b) -> m a -> m b
注意每对中这些类型之间的相似性。
我的问题是:liftM
(与liftA
不同)和ap
(与<*>
不同),只是<=>未设计为<=>的历史现实的结果>和<=>记住?或者它们是否与某些其他行为方式(可能是某些合法的<=>定义)不同于仅需要<=>上下文的版本?
如果它们不同,您能否提供一套简单的定义(遵守Typeclassopedia和其他地方描述的<=>,<=>,<=>和<=>定义所要求的法律,但不是由<=>和<=>表现不同的类型系统?
或者,如果它们不相同,你能否使用与前提相同的法则来证明它们的等同性?
解决方案
liftA
,liftM
,fmap
和.
应该都是相同的功能,如果它们满足仿函数法则必须 :
fmap id = id
但是,Haskell不会对此进行检查。
现在申请。对于某些仿函数而言,ap
和<*>
可能是不同的,因为可能有多个实现满足类型和规律。例如,List有多个可能的Applicative
实例。您可以按如下方式声明申请表:
instance Applicative [] where
(f:fs) <*> (x:xs) = f x : fs <*> xs
_ <*> _ = []
pure = repeat
liftM2 id
函数仍将被定义为Monad
,这是每个[]
免费提供的newtype ZipList a = ZipList [a]
实例。但是在这里你有一个具有多个ZipList
实例的类型构造函数的例子,这两个实例都满足法则。但是如果你的monad和你的应用函子不同意,那么为它们设置不同的类型被认为是好的形式。例如,上面的<=>实例与<=>的monad不一致,所以你应该说<=>然后为<=>而不是<=>创建新实例。
其他提示
他们可以不同,但他们不应该。
它们可以有所不同,因为它们可以有不同的实现:一个在instance Applicative
中定义,而另一个在instance Monad
中定义。但如果它们确实不同,那么我会说编写这些实例的程序员编写了误导性的代码。
你说得对:功能因历史原因而存在。人们对事情的发展方式有着强烈的想法。