Может ли liftM отличаться от liftA? - PullRequest
29 голосов
/ 28 октября 2009

Согласно 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

Обратите внимание на сходство этих типов в каждой паре.

Мой вопрос: liftM (в отличие от liftA) и ap (в отличие от <*>), просто результат исторической реальности, которую Monad не был разработан с Pointed и Applicative в виду? Или они каким-то другим поведенческим способом (возможно, для некоторых юридических Monad определений) отличаются от версий, которые требуют только Applicative контекста?

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

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

Ответы [ 2 ]

22 голосов
/ 28 октября 2009

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

fmap id = id

Однако Haskell не проверяет это.

Теперь для Applicative. Для некоторых функторов 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 вместо [].

8 голосов
/ 28 октября 2009

Они могут различаться, но они не должны .

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

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

...