Может ли liftM отличаться от liftA?
-
06-07-2019 - |
Вопрос
Согласно типэклассической энциклопедии (среди других источников), 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
.Но если они действительно отличаются, то я бы сказал, что программист, написавший эти экземпляры, написал вводящий в заблуждение код.
Вы правы:функции существуют в том виде, в каком они существуют, по историческим причинам.У людей есть твердые представления о том, как все должно было быть.