モナドを作るver. 0.1
>>> def Maybe(typ): class Foo(object): @classmethod def return_(_, x): return Just(x) @classmethod def bind(_, m, f): if isinstance(m, Nothing): return Nothing() elif isinstance(m, Just): return f(m.value) class Just(object): def __init__(self, x): assert isinstance(x, typ) self.value = x class Nothing(object): pass Foo.__name__ = "Maybe_%s" % typ.__name__ return Fooこれで型を受け取って型を返す関数ができました。 Maybe Intみたいに呼び出してみよう。
>>> Maybe(int) <class __main__.Maybe_int at 0x00D750F0>「Maybe Int」型ができました。
>>> MaybeInt = _ >>> MaybeInt.return_(1) <__main__.Just object at 0x00D57F70>「return 1」で「Just 1」ができます。 「MaybeInt.」と書かないといけないのは、Pythonに 引数の型の情報を使って適切な関数を選択する機能がないから。
適当な関数を作ってbindしてみましょう。
>>> def inc(x): #:: int -> Maybe int return MaybeInt.return_(x + 1) >>> MaybeInt.bind(_, inc) <__main__.Just object at 0x00D57A90> >>> _.value 2できました。
でも、これじゃIOモナドには進めません。 なぜかというと、x + 1の計算がbind時に行われているから。 ここの評価を遅延させる必要があります。
>>> class Lazy: def __init__(self, expr): self.expr = expr self.value = None def force(self): if self.value == None: print self.expr, "を評価します" self.value = self.expr.force() return self.value >>> class Literal: def __init__(self, value): self.value = value def force(self): return self.value >>> class LazyAdd(object): def __init__(self, a, b): self.a = a self.b = b self.value = None def force(self): if self.value == None: self.value = self.a.force() + self.b.force() return self.value >>> def inc(x): #:: int -> Maybe int return MaybeInt.return_(LazyAdd(x, Literal(1)))これで試そうと思ったのですけど、 intをLazyで包んでしまうと、 MaybeIntの型チェックで「intじゃなくてLazyだ」と怒られてしまうので 型チェックを削除。
ああ、でも本当は 「評価前」「評価後」という2状態じゃないんだなぁ。 パターンマッチの書かれ方によって、 どこまで評価するかが決まるのか…。
IOで「入力用モナドと出力用モナドをつなぐだけでwhileも書いていないのに ループしてくれる」 というような説明があるけども、 それは入出力用のモナドの中の実装が見えていないから魔法みたいに見えるだけ。
>>> def printStr(x): while True: v = x.force() if v == "[END]": break print v >>> def inputStr(): class LazyInput: def force(_): return raw_input() return LazyInput() >>> printStr(inputStr()) 111 111 222 222 [END]このコードの「printStr(inputStr())」だけ見せられれば、 そりゃ魔法みたいにも見える。