これまでPythonをメインに書いてきたのですが、最近Haskellを学んでいて、EitherはPythonでどう実装するんだろう、と思い自分で実装してみました。
実装
PythonによるEitherの実装を示します。
Functorとして扱えるように、fmapを実装しています。
※Monadとして扱うようにするためには、さらなる関数の実装が必要ですが、今後の課題としたいと思います。
from typing import Any, Callable
class Either:
pass
class Right(Either):
def __init__(self, v: Any):
self.__v = v
def value(self) -> Any:
return self.__v
class Left(Either):
def __init__(self, v: Any):
self.__v = v
def value(self) -> Any:
return self.__v
def fmap(f: Callable[[Any], Any], x: Either) -> Either:
'''
>>> fmap(lambda x: x + 1, Right(2)).value()
3
>>> fmap(lambda x: x + 1, Left('string')).value()
'string'
>>> fmap(lambda x: x + 1, Right('string')).value()
'must be str, not int'
'''
if not isinstance(f, Callable):
raise TypeError('f should be Callable instance')
if not isinstance(x, Either):
raise TypeError('x should be Either instance')
if isinstance(x, Right):
try:
return Right(f(x.value()))
except Exception as e:
return Left(str(e))
if isinstance(x, Left):
return x
if __name__ == '__main__':
import doctest
doctest.testmod()
構造としていたってシンプルです。
Eitherという親クラスを継承したRightクラスとLeftクラスを実装しています。
そして、Right、Leftクラスともに1つの値をコンストラクタの引数に渡し、その値を参照するvalueメソッドが実装されているのみです。
fmapでは第2引数がRightインスタンスのときはラップされた値に関数を適用し、結果をRightインスタンスで返す一方、Leftインスタンスの場合は、そのまま返すような実装となっています。
また、Pythonは動的型付け言語であるため、型チェックを自前で実装しています。
fはCallableであることをチェックしていますが、fの引数の型まではチェックできないため、不完全です。
もう少しいいやり方があるか考えてみようと思っています。
(いざ静的型付け言語を学んでみると動的型付け言語は機能不足に感じてきてしまいますね…)
まとめ
PythonでEitherの実装をしてみました。
先に述べた通り、Monadとして扱うようにするためには、さらなる関数の実装が必要なので、今後残りの関数の実装をしていきたいと思います。