[解決済み] モナドが合成の下では閉じていないことを示す具体例(証明付き)?
質問
適用型ファンクタは合成のもとで閉じているが、モナドは閉じていないことはよく知られている。 しかし、私はモナドが常に合成されるわけではないことを示す具体的な反例を見つけるのに苦労しています。
この回答
は
[String -> a]
を非モナドの例として挙げています。 少し遊んでみて、私は直感的にそれを信じていますが、この回答は正当な理由を与えることなく、ただ"joinは実装できません"と言っています。 もっと正式なものが欲しいです。 もちろん、型
[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
を定義する場合、2つの選択肢があります。
B Nothing
または
B (Just (P x x))
. を返す望みを持つためには、明らかなことです。
x
を返すためには、(1)と(2)から
x
を捨てられないので、2番目の選択肢を選ぶしかない。
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)の場合では、タイプ
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'')
関連
-
[解決済み] Haskell タプルをリスト化する?
-
[解決済み] Haskellで大規模設計?[クローズド]
-
[解決済み] IntとIntegerの違いは何ですか?
-
[解決済み] Haskell における `mod` と `rem` の違い
-
[解決済み] GHCiの複数行コマンド
-
[解決済み] Haskellデータ型のメモリフットプリント
-
[解決済み] Haskellってなんで流行ってるの?[クローズド]
-
[解決済み] 関数型プログラミングを実世界で使うには?[クローズド]
-
[解決済み] fixの使い方、効果について教えてください。
-
[解決済み] mtl、トランスフォーマー、monads-fd、monadLib、そして選択のパラドックス
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Haskell Preludeの'const'は何のためにあるのか?
-
[解決済み] Haskellは実世界で何に使われているのか?[クローズド]
-
[解決済み】Not a Functor/Functor/Applicative/Monadの良い例?
-
[解決済み] レコードの単一フィールドを割り当て、残りのフィールドはコピーするための省略記法?
-
[解決済み] HaskellとF#の主な違いは何ですか?[クローズド]
-
[解決済み] Haskellでグラフはどのように表現するのか?
-
[解決済み] Emacs Interactive-Haskell repl は、cabal と working directory のいずれかが project directory に設定されると無応答になる。
-
[解決済み] アプリケートは合成し、モナドは合成しない
-
[解決済み] 型チェッカーは非常に間違った型置換を許可しているが、プログラムはまだコンパイルできる
-
[解決済み] キュアリングを利用するためのパラメータの順序付け