Monad için izomorfik olan bu monadı düşünün (Bool ->)
:
data Pair a = P a a
instance Functor Pair where
fmap f (P x y) = P (f x) (f y)
instance Monad Pair where
return x = P x x
P a b >>= f = P x y
where P x _ = f a
P _ y = f b
ve Maybe
monad ile besteleyin :
newtype Bad a = B (Maybe (Pair a))
Bunun Bad
bir monad olamayacağını iddia ediyorum .
Kısmi kanıt:
fmap
Tatmin edici olanı tanımlamanın tek bir yolu var fmap id = id
:
instance Functor Bad where
fmap f (B x) = B $ fmap (fmap f) x
Monad yasalarını hatırlayın:
(1) join (return x) = x
(2) join (fmap return x) = x
(3) join (join x) = join (fmap join x)
Tanımı return x
için iki seçeneğimiz var: B Nothing
veya B (Just (P x x))
. x
(1) ve (2) 'den geri dönme ümidine sahip olmak için bir kenara atamayacağımız açıktır x
, bu yüzden ikinci seçeneği seçmeliyiz.
return' :: a -> Bad a
return' x = B (Just (P x x))
O bırakır join
. Yalnızca birkaç olası girdi olduğundan, her biri için bir durum oluşturabiliriz:
join :: Bad (Bad a) -> Bad a
(A) join (B Nothing) = ???
(B) join (B (Just (P (B Nothing) (B Nothing)))) = ???
(C) join (B (Just (P (B (Just (P x1 x2))) (B Nothing)))) = ???
(D) join (B (Just (P (B Nothing) (B (Just (P x1 x2)))))) = ???
(E) join (B (Just (P (B (Just (P x1 x2))) (B (Just (P x3 x4)))))) = ???
Çıktı türünü sahip olduğundan Bad a
, sadece seçenekleridir B Nothing
veya B (Just (P y1 y2))
nerede y1
, y2
seçilen lazım x1 ... x4
.
(A) ve (B) durumlarında tür değerimiz yoktur a
, bu nedenle B Nothing
her iki durumda da geri dönmek zorunda kalırız .
Durum (E), (1) ve (2) monad yasaları ile belirlenir:
join (return' (B (Just (P y1 y2))))
=
join (B (Just (P (B (Just (P y1 y2))) (B (Just (P y1 y2))))))
=
B (Just (P y1 y2))
Dönmek için B (Just (P y1 y2))
(e) 'de, bu araçlar, biz almak gerekir y1
birinden x1
ya da x3
, ve y2
gelen ya x2
ya da x4
.
join (fmap return' (B (Just (P y1 y2))))
=
join (B (Just (P (return y1) (return y2))))
=
join (B (Just (P (B (Just (P y1 y1))) (B (Just (P y2 y2))))))
=
B (Just (P y1 y2))
Aynı şekilde, bu biz almak gerektiğini söyledi y1
birinden x1
veya x2
ve y2
gelen ya x3
ya x4
. İkisini birleştirerek, (E) 'nin sağ tarafının olması gerektiğini belirledik B (Just (P x1 x4))
.
Şimdiye kadar her şey yolunda, ancak sorun (C) ve (D) için sağ tarafları doldurmaya çalıştığınızda ortaya çıkıyor.
Her biri için 5 olası sağ taraf vardır ve kombinasyonların hiçbiri çalışmaz. Henüz bunun için güzel bir argümanım yok, ancak tüm kombinasyonları kapsamlı bir şekilde test eden bir programım var:
{-# LANGUAGE ImpredicativeTypes, ScopedTypeVariables #-}
import Control.Monad (guard)
data Pair a = P a a
deriving (Eq, Show)
instance Functor Pair where
fmap f (P x y) = P (f x) (f y)
instance Monad Pair where
return x = P x x
P a b >>= f = P x y
where P x _ = f a
P _ y = f b
newtype Bad a = B (Maybe (Pair a))
deriving (Eq, Show)
instance Functor Bad where
fmap f (B x) = B $ fmap (fmap f) x
unit :: a -> Bad a
unit x = B (Just (P x x))
joins :: Integer
joins = sum $ do
let ways = [ \_ _ -> B Nothing
, \a b -> B (Just (P a a))
, \a b -> B (Just (P a b))
, \a b -> B (Just (P b a))
, \a b -> B (Just (P b b)) ] :: [forall a. a -> a -> Bad a]
c3 :: forall a. a -> a -> Bad a <- ways
c4 :: forall a. a -> a -> Bad a <- ways
let join :: forall a. Bad (Bad a) -> Bad a
join (B Nothing) = B Nothing
join (B (Just (P (B Nothing) (B Nothing)))) = B Nothing
join (B (Just (P (B (Just (P x1 x2))) (B Nothing)))) = c3 x1 x2
join (B (Just (P (B Nothing) (B (Just (P x3 x4)))))) = c4 x3 x4
join (B (Just (P (B (Just (P x1 x2))) (B (Just (P x3 x4)))))) = B (Just (P x1 x4))
guard $ all (\x -> join (unit x) == x) bad1
guard $ all (\x -> join (fmap unit x) == x) bad1
guard $ all (\x -> join (join x) == join (fmap join x)) bad3
return 1
main = putStrLn $ show joins ++ " combinations work."
bad1 :: [Bad Int]
bad1 = map fst (bad1' 1)
bad3 :: [Bad (Bad (Bad Int))]
bad3 = map fst (bad3' 1)
bad1' :: Int -> [(Bad Int, Int)]
bad1' n = [(B Nothing, n), (B (Just (P n (n+1))), n+2)]
bad2' :: Int -> [(Bad (Bad Int), Int)]
bad2' n = (B Nothing, n) : do
(x, n') <- bad1' n
(y, n'') <- bad1' n'
return (B (Just (P x y)), n'')
bad3' :: Int -> [(Bad (Bad (Bad Int)), Int)]
bad3' n = (B Nothing, n) : do
(x, n') <- bad2' n
(y, n'') <- bad2' n'
return (B (Just (P x y)), n'')
join
, iki monadın bileşimi için yazmanın imkansız olduğunu gösterir . genel . Ancak bu herhangi bir somut örneğe götürmez .