Может ли лифтМ отличать от liftA?
Согласно 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
Обратите внимание на сходство между этими типами в каждой паре.
Мой вопрос: are liftM
(в отличие от liftA
) и ap
(в отличие от <*>
), просто результат исторической реальности, что Monad
не был разработан с Pointed
и Applicative
в виду? Или они по каким-то другим поведенческим образом (потенциально, для некоторых легальных определений Monad
) отличаются от версий, для которых требуется только контекст Applicative
?
Если они различны, можете ли вы предоставить простой набор определений (подчиняясь законам, требуемым для Monad
, Applicative
, Pointed
и Functor
определений, описанных в Typeclassopedia и в других местах, но не выполняемых типа), для которых liftA
и liftM
ведут себя по-другому?
Альтернативно, если они не отличаются друг от друга, можете ли вы доказать их эквивалентность с использованием тех же законов, что и помещения?
Ответы
Ответ 1
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
вместо []
.
Ответ 2
Они могут отличаться, но они не должны.
Они могут различаться, потому что они могут иметь разные реализации: один определяется в instance Applicative
, а другой - в instance Monad
. Но если они действительно отличаются, то я бы сказал, что программист, который написал эти экземпляры, написал вводящий в заблуждение код.
Вы правы: функции существуют так же, как и по историческим причинам. У людей есть сильные идеи о том, как должно было быть.