パッケージ化しました
-> hachibeeDI/letexpr
ネタです
PythonでStateモナド
の記事を書いていて、PythonでもHaskellみたいなlet式が欲しいなって思ったので考えてみました。
そしたら、関連する投稿に
letを作ってLet's 1行プログラミング
というものがあったので、流用していい感じにします。
と思ったのですが、ざっと見た感じこれはapplyっぽいので流用は断念しました。なのでがんばります。
let式は、let内で束縛を行い、inで式を評価するようです。
また、let内で束縛した変数はlet式全体から参照することが出来るみたいです。
とりあえず結果を変数的な何かに束縛して、inで使えるようにしましょう
class let(object):
def __init__(self, action=None):
if action is None:
name, act = action
self.lets = {name: act()}
else:
self.lets = {}
def __or__(self, action):
''' :type action: (str, func) '''
name, f = action
self.lets[name] = f()
return self
def in_(self, func):
return func(**self.lets)
if __name__ == '__main__':
x = let(('xx', lambda : 1 + 2)) | ('y', lambda : 'yyy') | ('z', lambda : 5)
print x.in_(lambda xx, y, z: str(xx) + y + str(z))
一応形になりました。
ですが、このままだとlet式同士での値の参照が出来ないのでいい感じにしてやります。
上記のように、Pythonでは関数の引数に対して**辞書
を渡すことで、keyに対応する引数にvalueを渡すことが出来ますが、いくつかの評判の良い言語などとは違い引数の過不足に対してはエラーを吐いてきます。
つまり、self.lets_
内に保存している結果の中で、各々が必要としている値のみを渡す必要があるわけです。
こういうときinspect
モジュールが便利なんですよ、ということでgetargspec
関数を使って必要な引数のみ渡してやります。
from inspect import getargspec
class let(object):
def __init__(self, action=None):
if action is not None:
name, act = action
self.lets = {name: act()}
else:
self.lets = {}
def __or__(self, action):
''' :type action: (str, func) '''
name, f = action
require_arg_keys = getargspec(f).args
self.lets[name] = f(
**self.__extract_require_args(require_arg_keys)
)
return self
def __extract_require_args(self, arg_keys):
return {k: v for k, v in self.lets.iteritems() if k in arg_keys}
def in_(self, func):
require_arg_keys = getargspec(func).args
return func(
**self.__extract_require_args(require_arg_keys)
)
では、実際に色々な引数を要求する式を渡して試してみます。
インデントとかもカッコよくしましょう。
measure = \
(let()
| ('x', lambda : 10)
| ('y', lambda : 20)
| ('size', lambda x, y: x * y)
| ('hoge', lambda x, y: 'fooo')
) \
.in_(lambda x, y, size:
'x = {x}, y = {y}, x * y = {size}'.format(x=x, y=y, size=size))
print measure # => x = 10, y = 20, x * y = 200
やりましたね。
現状だと、上から下に評価していってるので、下にあるラムダから上の値を取得することは出来ませんが、頑張って式の評価を遅延させたりすればそれも出来るようになりそうですね。
もっとも普通に書いた方が普通に読めるので普通が一番だと思いました。