リストモナドをPythonで再現したい part 2
前回のおさらい
Python で Haskell のリストモナドを再現しました。
def foldr(f, z, xxs):
if xxs == []:
return z
x = xxs[0]
xs = xxs[1:]
return f(x, foldr(f, z, xs))
def concat(xxs):
plus = lambda x, y: x + y
return foldr(plus, [], xxs)
def ret(a):
"""
return :: a -> m a の代わり
"""
return [a]
def bind(ma, a_to_mb):
"""
(>>=) :: m a -> ( a -> m b) -> m b の代わり
"""
return concat(list(map(a_to_mb, ma)))
前回は以下の一文で締めました。
次回はこれを満たすことを確認したいと思います。
今回はこれを確認したいと思います。
モナド則を満たすのか?
モナドは以下を満たしていないといけません。
-
return a >>= f
とf a
は等価 -
m >>= return
とm
は等価 -
(m >>= f) >>= g
とm >>= (\x -> f x >>= g)
は等価
一般的な証明をするわけにもいかないので、簡単な具体例で確認します。
とはいえ、まず Python で無理やり実装しているので、ちょっとした読み替えが必要です。
-
return a
はret(a)
と読み替えられます。 -
ma >>= f
はbind(ma, f)
と読み替えられます。
return a >>= f
と f a
が等価
a
と f
を以下とします。
>>> a = 2
>>> f = lambda x: [x, x * 2]
return a >>= f
は bind(ret(a), f)
で表せます。
すると、
>>> bind(ret(a), f)
[2, 4]
>>> f(a)
[2, 4]
合ってますね。
a
を文字列にしても大丈夫なようです。
>>> a = "ABC"
>>> f = lambda x: [x, x * 2]
>>> bind(ret(a), f)
['ABC', 'ABCABC']
>>> f(a)
['ABC', 'ABCABC']
m >>= return
と m
が等価
同じく a
と f
を以下とします。
>>> a = 2
>>> f = lambda x: [x, x * 2]
m
はただリストにぶち込むだけなので、こうなります。(ですよね???)
def m(a):
return [a]
return
と同じですが、多分合ってると思います。
ここで m
は関数ですが、そのまま使うのは難しいので何かしらの a
に適用した結果を使います。
>>> bind(m(a), ret)
[2]
>>> m(a)
[2]
大丈夫そうです。
文字列版も試します。
>>> a = "ABC"
>>> f = lambda x: [x, x * 2]
>>> bind(ret(a), f)
['ABC']
>>> m(a)
['ABC']
同じく大丈夫そうです。
(m >>= f) >>= g
と m >>= (\x -> f x >>= g)
が等価
最後です。
>>> a = 2
>>> f = lambda x: [x, x * 2]
>>> g = lambda x: [x + 1, x + 5]
ちょっと式が複雑になります。
最初は(m >>= f) >>= g
の表現です。
m >>= f
は bind(m, f)
ですが、これも具体的な値 a
を入れることにします。
すると bind(m(a), f)
となりますから、これを bind(XXXX, g)
の XXXX の場所に入れて bind(bind(m(a), f), g)
が得られます。
もう一方 m >>= (\x -> f x >>= g)
は、 bind(m(a), YYYY)
の YYYY の場所に (\x -> f x >>= g)
つまり lambda x: bind((f), g)
を代入すれば良いので bind(m(a), lambda x: bind(f(x), g))
です。
実際に計算させると
>>> bind(bind(m(a), f), g)
[3, 7, 5, 9]
>>> bind(m(a), lambda x: bind(f(x), g))
[3, 7, 5, 9]
一致します。
文字列バージョンも試します。
>>> a = "ABC"
>>> f = lambda x: [x, x * 2]
>>> g = lambda x: [x + "p", x + "q"]
>>> bind(bind(m(a), f), g)
['ABCp', 'ABCq', 'ABCABCp', 'ABCABCq']
>>> bind(m(a), lambda x: bind(f(x), g))
['ABCp', 'ABCq', 'ABCABCp', 'ABCABCq']
一致します。
次は do 記法の再現か?
やりたい・・・!
Discussion