응용 펑 터는 컴포지션에서 닫히지 만 모나드는 그렇지 않다는 것은 잘 알려져 있습니다. 그러나 모나드가 항상 구성하는 것은 아니라는 구체적인 반례를 찾는 데 어려움을 겪었습니다.
이 대답 은 [String -> a]
비 모나드의 예입니다. 잠시 놀아 본 후 직관적으로 믿었지만 그 대답은 실제로 정당화하지 않고 “조인을 구현할 수 없습니다”라고 말합니다. 좀 더 형식적인 것을 원합니다. 물론 type과 함께 많은 함수가 있습니다 [String -> [String -> a]] -> [String -> a]
. 그러한 함수가 반드시 모나드 법칙을 만족하지 않는다는 것을 보여 주어야합니다.
모든 예 (증거 포함)가 가능합니다. 특히 위의 예에 대한 증거를 찾는 것은 아닙니다.
답변
모나드와 동형 인 다음 모나드를 고려하십시오 (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
Maybe
모나드로 구성합니다 .
newtype Bad a = B (Maybe (Pair a))
나는 그것이 Bad
모나드가 될 수 없다고 주장합니다 .
부분 증명 :
다음 fmap
을 충족시키는 정의 하는 방법은 하나뿐입니다 fmap id = id
.
instance Functor Bad where
fmap f (B x) = B $ fmap (fmap f) x
모나드 법칙을 상기하십시오.
(1) join (return x) = x
(2) join (fmap return x) = x
(3) join (join x) = join (fmap join x)
의 정의 return x
에는 두 가지 선택이 있습니다. B Nothing
또는 B (Just (P x x))
. x
(1)과 (2)에서 돌아올 희망을 가지려면 버릴 수 없으므로 x
두 번째 옵션을 선택해야합니다.
return' :: a -> Bad a
return' x = B (Just (P x x))
그게 떠난다 join
. 가능한 입력이 적기 때문에 각각에 대해 케이스를 만들 수 있습니다.
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)))))) = ???
출력 형식을 갖기 때문에 Bad a
, 유일한 옵션은 B Nothing
또는 B (Just (P y1 y2))
여기서 y1
, y2
로부터 선택되어야한다 x1 ... x4
.
(A)와 (B)의 경우에는 type 값이 없으므로 두 경우 모두 a
반환해야합니다 B Nothing
.
사례 (E)는 (1) 및 (2) 모나드 법칙에 의해 결정됩니다.
-- apply (1) to (B (Just (P y1 y2)))
join (return' (B (Just (P y1 y2))))
= -- using our definition of return'
join (B (Just (P (B (Just (P y1 y2))) (B (Just (P y1 y2))))))
= -- from (1) this should equal
B (Just (P y1 y2))
반환하기 위해 B (Just (P y1 y2))
케이스 (E)에서,이 방법은 우리가 선택해야 y1
하나에서 x1
나 x3
, 그리고 y2
에서 하나 x2
또는 x4
.
-- apply (2) to (B (Just (P y1 y2)))
join (fmap return' (B (Just (P y1 y2))))
= -- def of fmap
join (B (Just (P (return y1) (return y2))))
= -- def of return
join (B (Just (P (B (Just (P y1 y1))) (B (Just (P y2 y2))))))
= -- from (2) this should equal
B (Just (P y1 y2))
마찬가지로, 이것은 우리가 선택해야한다고 말한다 y1
하나에서 x1
나 x2
, 그리고 y2
에서 하나 x3
또는 x4
. 두 가지를 결합하여 (E)의 오른쪽이이어야 함을 결정합니다 B (Just (P x1 x4))
.
지금까지는 모두 좋지만 (C)와 (D)의 오른쪽을 채우려 고 할 때 문제가 발생합니다.
각각에 대해 5 개의 가능한 오른편이 있으며 어떤 조합도 작동하지 않습니다. 아직 이에 대한 좋은 주장은 없지만 모든 조합을 철저히 테스트하는 프로그램이 있습니다.
{-# 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
-- The only definition that could possibly work.
unit :: a -> Bad a
unit x = B (Just (P x x))
-- Number of possible definitions of join for this type. If this equals zero, no monad for you!
joins :: Integer
joins = sum $ do
-- Try all possible ways of handling cases 3 and 4 in the definition of join below.
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 -- no choice
join (B (Just (P (B Nothing) (B Nothing)))) = B Nothing -- again, no choice
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)) -- derived from monad laws
-- We've already learnt all we can from these two, but I decided to leave them in anyway.
guard $ all (\x -> join (unit x) == x) bad1
guard $ all (\x -> join (fmap unit x) == x) bad1
-- This is the one that matters
guard $ all (\x -> join (join x) == join (fmap join x)) bad3
return 1
main = putStrLn $ show joins ++ " combinations work."
-- Functions for making all the different forms of Bad values containing distinct Ints.
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'')
답변
작은 구체적인 반례의 경우 터미널 모나드를 고려하십시오.
data Thud x = Thud
return
및 >>=
바로 가서 Thud
하고, 법은 하찮게십시오.
이제 Bool에 대한 라이터 모나드 (예를 들어 xor-monoid 구조)도 만들어 보겠습니다.
data Flip x = Flip Bool x
instance Monad Flip where
return x = Flip False x
Flip False x >>= f = f x
Flip True x >>= f = Flip (not b) y where Flip b y = f x
어, 음, 작곡이 필요해
newtype (:.:) f g x = C (f (g x))
이제 정의 해보세요 …
instance Monad (Flip :.: Thud) where -- that's effectively the constant `Bool` functor
return x = C (Flip ??? Thud)
...
Parametricity는 ???
에 어떤 유용한 방식으로도 의존 할 수 없으므로 x
상수 여야합니다. 결과적으로 join . return
반드시 일정한 기능이므로 법칙은
join . return = id
정의가 무엇이든 실패해야 join
하며 return
우리는 선택합니다.
답변
제외 된 중간 구성
(->) r
모든위한 모나드입니다 r
및 Either e
모든위한 모나드이다 e
. 구성 ( (->) r
내부, Either e
외부)을 정의 해 보겠습니다 .
import Control.Monad
newtype Comp r e a = Comp { uncomp :: Either e (r -> a) }
내가 주장 하는 경우 Comp r e
모든위한 모나드했다 r
그리고 e
우리가 깨달을 수 exluded 중간의 법을 . 이것은 기능 언어의 유형 시스템의 기초가되는 직관적 논리 에서는 불가능 합니다 (중간 제외의 법칙을 갖는 것은 call / cc 연산자 를 갖는 것과 동일합니다 ).
Comp
모나드 라고 가정 해 봅시다 . 그런 다음 우리는
join :: Comp r e (Comp r e a) -> Comp r e a
그래서 우리는 정의 할 수 있습니다
swap :: (r -> Either e a) -> Either e (r -> a)
swap = uncomp . join . Comp . return . liftM (Comp . liftM return)
(이것은 Brent가 언급 swap
한 문서 작성 모나드 , 섹션 4.3의 기능 일 뿐이며 newtype의 (de) 생성자가 추가 된 경우에만 해당됩니다. 어떤 속성이 있는지는 신경 쓰지 않습니다. 중요한 것은 정의 가능하고 총체적이라는 것입니다. .)
이제 설정하겠습니다
data False -- an empty datatype corresponding to logical false
type Neg a = (a -> False) -- corresponds to logical negation
r = b
,, e = b
에 대한 스왑 전문화 a = False
:
excludedMiddle :: Either b (Neg b)
excludedMiddle = swap Left
결론 :(->) r
및 Either r
모나드 임에도 불구 하고 그 구성 Comp r r
은 그럴 수 없습니다.
참고 : 이는 정의 방법 ReaderT
과 EitherT
정의 에도 반영 됩니다. 모두 ReaderT r (Either e)
와 EitherT e (Reader r)
동형이다 r -> Either e a
! dual에 대한 모나드를 정의하는 방법은 없습니다 Either e (r -> a)
.
탈출 IO
행동
같은 맥락에서 어떤 식 으로든 IO
탈출로 이어지는 많은 예가 있습니다 IO
. 예를 들면 :
newtype Comp r a = Comp { uncomp :: IO (r -> a) }
swap :: (r -> IO a) -> IO (r -> a)
swap = uncomp . join . Comp . return . liftM (Comp . liftM return)
이제 가자
main :: IO ()
main = do
let foo True = print "First" >> return 1
foo False = print "Second" >> return 2
f <- swap foo
input <- readLn
print (f input)
이 프로그램이 실행되면 어떻게됩니까? 두 가지 가능성이 있습니다.
- 콘솔에서 읽은 후 “First”또는 “Second”가 인쇄
input
됩니다. 즉, 일련의 작업이 반전 되었고 해당 작업이foo
pure로 이스케이프 되었음을 의미합니다f
. -
또는
swap
(따라서join
)IO
조치를 버리고 “First”또는 “Second”가 인쇄되지 않습니다. 그러나 이것은join
법 을 위반 한다는 것을 의미합니다join . return = id
경우 때문에
join
던져IO
멀리 조치를 한 후foo ≠ (join . return) foo
다른 유사한 IO
+ 모나드 조합으로 인해
swapEither :: IO (Either e a) -> Either e (IO a)
swapWriter :: (Monoid e) => IO (Writer e a) -> Writer e (IO a)
swapState :: IO (State e a) -> State e (IO a)
...
그들의 join
구현은 e
탈출을 허용 IO
하거나 그것을 버리고 다른 것으로 대체하여 법을 위반해야합니다.
답변
링크에서이 데이터 유형을 참조하므로 특정 구현을 선택해 보겠습니다. data A3 a = A3 (A1 (A2 a))
나는 임의로 선택할 A1 = IO, A2 = []
것이다. 우리는 또한 그것을 a로 만들고 newtype
재미를 위해 특별히 뾰족한 이름을 줄 것입니다.
newtype ListT IO a = ListT (IO [a])
해당 유형에서 임의의 작업을 생각해 내고 서로 다르지만 동일한 두 가지 방법으로 실행 해 보겠습니다.
λ> let v n = ListT $ do {putStr (show n); return [0, 1]}
λ> runListT $ ((v >=> v) >=> v) 0
0010101[0,1,0,1,0,1,0,1]
λ> runListT $ (v >=> (v >=> v)) 0
0001101[0,1,0,1,0,1,0,1]
보시다시피 이것은 연관성 법칙을 위반합니다 : ∀x y z. (x >=> y) >=> z == x >=> (y >=> z)
.
그것은 밝혀 ListT m
경우에만 모나드 인 m
A는 교환 법칙이 성립 모나드. 이것은 []
“두 개의 임의의 모나드를 구성하면 모나드를 생성한다”는 보편적 인 규칙을 깨뜨리는 큰 범주의 모나드가로 구성되는 것을 방지합니다 .
답변
모나드는 구성하지 않습니다. 일반적인 방법이 아닙니다. 모나드를 구성하는 일반적인 방법이 없습니다. 참조 https://www.slideshare.net/pjschwarz/monads-do-not-compose를