liftM 与 liftA 有什么不同吗?

发布于 2024-08-09 14:36:23 字数 2018 浏览 6 评论 0原文

根据Typeclassopedia(以及其他来源),Applicative 逻辑上属于类型类层次结构中的 MonadPointed (以及 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 (与 <*> 不同) >),仅仅是历史现实的结果,即 Monad 在设计时没有考虑到 PointedApplicative?或者它们是否以某种其他行为方式(可能对于某些合法的 Monad 定义)与仅需要 Applicative 上下文的版本不同?

如果它们不同,您能否提供一组简单的定义(遵守 MonadApplicativePointed函子定义在Typeclassopedia和其他地方描述,但不是由类型系统强制执行的),其中liftAliftM的行为不同?

或者,如果它们不不同,您能否使用相同的定律作为前提来证明它们的等价性?

According to the Typeclassopedia (among other sources), Applicative logically belongs between Monad and Pointed (and thus Functor) in the type class hierarchy, so we would ideally have something like this if the Haskell prelude were written today:

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

(Where those default definitions were re-typed by me from the explanation at Wikipedia, errors being my own, but if there are errors it is at least in principle possible.)

As the libraries are currently defined, we have:

liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftM ::       (Monad m) => (a -> b) -> m a -> m b

and:

(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
ap    ::       (Monad m) => m (a -> b) -> m a -> m b

Note the similarity between these types within each pair.

My question is: are liftM (as distinct from liftA) and ap (as distinct from <*>), simply a result of the historical reality that Monad wasn't designed with Pointed and Applicative in mind? Or are they in some other behavioral way (potentially, for some legal Monad definitions) distinct from the versions that only require an Applicative context?

If they are distinct, could you provide a simple set of definitions (obeying the laws required of Monad, Applicative, Pointed, and Functor definitions described in the Typeclassopedia and elsewhere but not enforced by the type system) for which liftA and liftM behave differently?

Alternatively, if they are not distinct, could you prove their equivalence using those same laws as premises?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

赠佳期 2024-08-16 14:36:23

liftAliftMfmap. 应该都是相同的函数,如果它们满足函子定律,则它们必须

fmap id = id

但是,Haskell 不会检查这一点。

现在来说说应用性的。对于某些函子来说,ap<*> 可能是不同的,因为可能有多个实现满足类型和定律。例如,List 有多个可能的 Applicative 实例。您可以按如下方式声明应用程序:

instance Applicative [] where
  (f:fs) <*> (x:xs) = f x : fs <*> xs
  _      <*> _      = []
  pure              = repeat

ap 函数仍将定义为 liftM2 id,这是免费提供的 Applicative 实例每个Monad。但这里有一个类型构造函数的示例,该构造函数具有多个 Applicative 实例,这两个实例都满足定律。但是,如果您的单子和应用函子不一致,那么为它们设置不同的类型被认为是一种很好的形式。例如,上面的 Applicative 实例与 [] 的 monad 不一致,因此您应该说 newtype ZipList a = ZipList [a] 然后为 ZipList 创建新实例,而不是 []

liftA, liftM, fmap, and . should all be the same function, and they must be if they satisfy the functor law:

fmap id = id

However, this is not checked by Haskell.

Now for Applicative. It's possible for ap and <*> to be distinct for some functors simply because there could be more than one implementation that satisfies the types and the laws. For example, List has more than one possible Applicative instance. You could declare an applicative as follows:

instance Applicative [] where
  (f:fs) <*> (x:xs) = f x : fs <*> xs
  _      <*> _      = []
  pure              = repeat

The ap function would still be defined as liftM2 id, which is the Applicative instance that comes for free with every Monad. But here you have an example of a type constructor having more than one Applicative instance, both of which satisfy the laws. But if your monads and your applicative functors disagree, it's considered good form to have different types for them. For example, the Applicative instance above does not agree with the monad for [], so you should really say newtype ZipList a = ZipList [a] and then make the new instance for ZipList instead of [].

dawn曙光 2024-08-16 14:36:23

它们可以不同,但不应该

它们可能有所不同,因为它们可以有不同的实现:一个是在实例 Applicative 中定义,而另一个是在实例 Monad 中定义。但如果它们确实不同,那么我会说编写这些实例的程序员编写了误导性代码。

你是对的:这些功能的存在是出于历史原因。人们对于事情应该如何发展有着强烈的想法。

They can differ, but they shouldn't.

They can differ because they can have different implementations: one is defined in an instance Applicative while the other is defined in an instance Monad. But if they indeed differ, then I'd say the programmer who wrote those instances wrote misleading code.

You are right: the functions exist as they do for historical reasons. People have strong ideas about how things should have been.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文