Что является примером Monad, который является Альтернативой, но не MonadPlus?
В своем ответе на вопрос "Различие между классами типов MonadPlus
, Alternative
, а также Monoid
? ", Говорит Эдвард Кметт
Более того, даже если
Applicative
был суперклассомMonad
Вы бы нуждались вMonadPlus
класс в любом случае, потому что подчиняясьempty <*> m = empty
не достаточно строго, чтобы доказать это
empty >>= f = empty
Так утверждая, что что-то
MonadPlus
сильнее, чем утверждать, что этоAlternative
,
Ясно, что любой аппликативный функтор, который не является монадой, автоматически является примером Alternative
который не является MonadPlus
, но ответ Эдварда Кметта подразумевает, что существует монада, которая является Alternative
но не MonadPlus
: его empty
а также <|>
будет удовлетворять Alternative
законы, 1 но не MonadPlus
законы. 2 Я не могу придумать пример этого сам; кто-нибудь знает об этом?
1 Я не смог найти каноническую ссылку для набора Alternative
законы, но я излагаю то, что, по моему мнению, примерно на полпути в моем ответе на вопрос "Смущен смыслом Alternative
класс типов и его связь с другими типами классов " (найдите фразу" правильная дистрибутивность "). Четыре закона, которые я считаю необходимыми:
- Правильное распределение (из
<*>
):(f <|> g) <*> a = (f <*> a) <|> (g <*> a)
- Правильное поглощение (для
<*>
):empty <*> a = empty
- Левая дистрибутивность (из
fmap
):f <$> (a <|> b) = (f <$> a) <|> (f <$> b)
- Левое поглощение (для
fmap
):f <$> empty = empty
Я также с радостью согласился бы получить более полезный набор Alternative
законы.
2 Я знаю, что есть некоторая двусмысленность в том, что MonadPlus
законы есть; Я доволен ответом, в котором используется левый дистрибутив или левый улов, хотя я бы слабо предпочел первый.
1 ответ
Подсказка к вашему ответу есть в HaskellWiki о MonadPlus, на который вы ссылаетесь:
Какие правила? Martin & Gibbons выбирают Monoid, Left Zero и Left Distribution. Это делает
[]
MonadPlus, но неMaybe
или жеIO
,
Так что в соответствии с вашим любимым выбором, Maybe
не MonadPlus (хотя есть экземпляр, он не удовлетворяет левому распределению). Давайте докажем, что удовлетворяет Альтернатива.
Maybe
альтернатива
- Правильное распределение (из
<*>
):(f <|> g) <*> a = (f <*> a) <|> (g <*> a)
Случай 1: f=Nothing
:
(Nothing <|> g) <*> a = (g) <*> a -- left identity <|>
= Nothing <|> (g <*> a) -- left identity <|>
= (Nothing <*> a) <|> (g <*> a) -- left failure <*>
Случай 2: a=Nothing
:
(f <|> g) <*> Nothing = Nothing -- right failure <*>
= Nothing <|> Nothing -- left identity <|>
= (f <*> Nothing) <|> (g <*> Nothing) -- right failure <*>
Случай 3: f=Just h, a = Just x
(Just h <|> g) <*> Just x = Just h <*> Just x -- left bias <|>
= Just (h x) -- success <*>
= Just (h x) <|> (g <*> Just x) -- left bias <|>
= (Just h <*> Just x) <|> (g <*> Just x) -- success <*>
- Правильное поглощение (для
<*>
):empty <*> a = empty
Это легко, потому что
Nothing <*> a = Nothing -- left failure <*>
- Левая дистрибутивность (из
fmap
):f <$> (a <|> b) = (f <$> a) <|> (f <$> b)
Случай 1: a = Nothing
f <$> (Nothing <|> b) = f <$> b -- left identity <|>
= Nothing <|> (f <$> b) -- left identity <|>
= (f <$> Nothing) <|> (f <$> b) -- failure <$>
Случай 2: a = Just x
f <$> (Just x <|> b) = f <$> Just x -- left bias <|>
= Just (f x) -- success <$>
= Just (f x) <|> (f <$> b) -- left bias <|>
= (f <$> Just x) <|> (f <$> b) -- success <$>
- Левое поглощение (для
fmap
):f <$> empty = empty
Еще один легкий:
f <$> Nothing = Nothing -- failure <$>
Maybe
не MonadPlus
Давайте докажем утверждение, что Maybe
не MonadPlus: мы должны показать, что mplus a b >>= k = mplus (a >>= k) (b >>= k)
не всегда держится Хитрость заключается в том, чтобы, как всегда, использовать некоторую привязку, чтобы скрыть очень разные значения:
a = Just False
b = Just True
k True = Just "Made it!"
k False = Nothing
Сейчас
mplus (Just False) (Just True) >>= k = Just False >>= k
= k False
= Nothing
здесь я использовал связывание (>>=)
вырвать неудачу (Nothing
) из челюстей победы, потому что Just False
выглядело как успех.
mplus (Just False >>= k) (Just True >>= k) = mplus (k False) (k True)
= mplus Nothing (Just "Made it!")
= Just "Made it!"
Здесь провал (k False
) был рассчитан рано, поэтому проигнорировали и мы "Made it!"
,
Так, mplus a b >>= k = Nothing
но mplus (a >>= k) (b >>= k) = Just "Made it!"
,
Вы можете посмотреть на это как я, используя >>=
сломать левое смещение mplus
за Maybe
,
Обоснованность моих доказательств:
На тот случай, если вы почувствуете, что я не сделал достаточно утомительного вывода, я докажу, какие идентификаторы я использовал:
во-первых
Nothing <|> c = c -- left identity <|>
Just d <|> c = Just d -- left bias <|>
которые приходят из объявления экземпляра
instance Alternative Maybe where
empty = Nothing
Nothing <|> r = r
l <|> _ = l
во-вторых
f <$> Nothing = Nothing -- failure <$>
f <$> Just x = Just (f x) -- success <$>
которые только что пришли (<$>) = fmap
а также
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap f (Just a) = Just (f a)
В-третьих, остальные три требуют немного больше работы:
Nothing <*> c = Nothing -- left failure <*>
c <*> Nothing = Nothing -- right failure <*>
Just f <*> Just x = Just (f x) -- success <*>
Что происходит из определений
instance Applicative Maybe where
pure = return
(<*>) = ap
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap = liftM2 id
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }
instance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= _ = Nothing
return = Just
так
mf <*> mx = ap mf mx
= liftM2 id mf mx
= do { f <- mf; x <- mx; return (id f x) }
= do { f <- mf; x <- mx; return (f x) }
= do { f <- mf; x <- mx; Just (f x) }
= mf >>= \f ->
mx >>= \x ->
Just (f x)
так что если mf
или же mx
Ничего, результат тоже Nothing
тогда как если mf = Just f
а также mx = Just x
результат Just (f x)