Вопрос

Согласно типэклассической энциклопедии (среди других источников), 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 (в отличие от <*>), просто результат исторической реальности, которая Monad не был разработан с Pointed и Applicative в уме?Или они каким-то другим поведенческим образом (потенциально, для некоторых юридических Monad определения), отличные от версий, которые требуют только Applicative контекст?

Если они различны, не могли бы вы предоставить простой набор определений (в соответствии с законами, требуемыми Monad, Applicative, Pointed, и Functor определения, описанные в Typeclassopedia и в других местах, но не применяемые системой типов), для которых liftA и liftM вести себя по-другому?

В качестве альтернативы, если они не различны, не могли бы вы доказать их эквивалентность, используя те же законы, что и предпосылки?

Это было полезно?

Решение

liftA, liftM, fmap, и . следует все они выполняют одну и ту же функцию, и они должен быть, если они удовлетворяют закону функтора:

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 приведенный выше пример не согласен с монадой для [], так что вы действительно должны сказать newtype ZipList a = ZipList [a] а затем создайте новый экземпляр для ZipList вместо того , чтобы [].

Другие советы

Они может отличаются, но они не должен.

Они могут отличаться, потому что у них могут быть разные реализации:один из них определен в instance Applicative в то время как другой определен в instance Monad.Но если они действительно отличаются, то я бы сказал, что программист, написавший эти экземпляры, написал вводящий в заблуждение код.

Вы правы:функции существуют в том виде, в каком они существуют, по историческим причинам.У людей есть твердые представления о том, как все должно было быть.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top