【问题标题】:Can liftM differ from liftA?liftM 可以与 liftA 不同吗?
【发布时间】:2009-10-28 02:34:44
【问题描述】:

根据the 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

(这些默认定义是我从explanation at Wikipedia 重新键入的,错误是我自己的,但如果有错误,至少原则上是可能的。)

根据当前定义的库,我们有:

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(不同于&lt;*&gt;),仅仅是Monad不是用@987654335设计的历史现实的结果@ 和 Applicative 记住了吗?或者它们是否以某种其他行为方式(可能对于某些合法的Monad 定义)与只需要Applicative 上下文的版本不同?

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

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

【问题讨论】:

    标签: haskell theory typeclass category-theory


    【解决方案1】:

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

    fmap id = id
    

    但是,Haskell 不会对此进行检查。

    现在申请。 ap&lt;*&gt; 对于某些函子来说可能是不同的,因为可能有多个实现满足类型和规律。例如,List 有多个可能的Applicative 实例。您可以如下声明一个应用程序:

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

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

    【讨论】:

    • 好的,没错,对于某些类型,存在不止一个 Applicative 实例和不止一个 Monad 实例。但是,根据上述定义,纯/返回实现必须在 Applicative 实例和 Monad 实例之间共享。这是否意味着,为了满足法律, 和 ap 必须相同?看起来您的示例依赖于使用 pure = repeat 为 [] 创建一个 Applicative 实例,但仍然使用带有 return = ([]) 的 [] 的 Prelude Monad 实例?
    • 我可能遗漏了一些东西,但 forall a,b. (a -&gt; b) -&gt; F a -&gt; F b 类型的函数不能保证为 fmap。例如,考虑notmap f xs = zipWith ($) (repeat (const . f $ head xs)) xs
    • @Apocalisp:仅考虑 fmap 的类型,您仍然可以拥有外来成员。附带条件 fmap id = id 足以迫使问题发生,我认为您在“f”上输入的额外量词是作弊。 ;) Doug,虽然 Functor 的定义是独一无二的,但 Applicative 法则足够宽松,可以允许多个兼容的定义。但是,按照惯例,应该使给定类型的 Monad 和 Applicative 兼容。
    • 为什么第一段提到..fmap (或其他两个)的特定实例,它根本不能等于例如liftM 应用在 IO monad 的上下文中,对吧?
    【解决方案2】:

    他们可以不同,但他们不应该

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

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

    【讨论】:

      猜你喜欢
      • 2021-11-18
      • 2019-05-17
      • 1970-01-01
      • 2015-11-29
      • 2020-12-27
      • 2014-01-21
      • 2014-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多