私はいくつかのPythonコードをHaskellに変換しています。ビジネスロジックはかなり複雑で、私のHaskellコードは醜いものになっています。アクションを収集し、必要に応じて早く分岐する
私のPython関数:
def f(some_state):
doAction1() # e.g. send_message("Hi there!")
if not userIsAuthenticated:
doAction2() # e.g. send_message("Please login")
return
if not userHasPermission:
doAction3() # e.g. send_message("Please sudo")
return
if somePredicate3:
doAction4()
if somePredicate4:
doAction5()
return
私はHaskellのバージョンのための2つの制約があります。可能な限り純粋な私のコードを保つために
- を、私は内のすべての私の行動を収集したいと思いますリスト(または私が気に入っているならFree Monad)して(関数外で)それを実行します。 Pythonでは、私の関数の冒頭に
tmp = []
を定義し、tmp
に(呼び出しを行わずに)すべてのアクションを追加し、tmp
を返します。それから、私はすべてのアクションを実行するためにリストを繰り返します(Pythonicではなく!)。 - ないこのようなプッシュ・ツー・右狂気で終わる:
- 基本的に、私は、必要に応じて早期に行動して終了のリストを収集する
if a
then undefined
else if b
then undefined
else if c
then undefined
else if d
then undefined
else undefined
私のような何かを書くことができるように:
tell Action1
when somePredicate1 $ tell doAction2
when somePredicate2 $ tell doAction3
...
tell
はライターモナドからのものではないが、それはfunctioです(これは存在しない)nは、これまでに "言われた"すべてのものを集めて(àla writer monad)、終了時に返されます。
これは、最初の左(そして左の値)の前に起こったすべてのアクションを保持し、最初の左から早く終了する必要があることを除いて、一般化されたモナドのように実際に感じます。私は、おそらくMonadPlusやAlternativeを使って解決策を作っていると思っていますが、何か素敵なことは分かりません。基本的には、ファンシーなguard
。
私はあまりにも多くの楽しみを持っていることは間違いありませんが、私は自分のコードが正しくドリフトすることを受け入れるべきです。しかしそれはエレガントではないでしょうか?
編集:わかりやすくするために、タイプf :: State -> [Action]
の機能が必要です(data Action = Action1 | Action2 | Action3
)。
おかげで、それは素敵なソリューションです:)私は返すように 'unlock'を'たいと思いますが、[アクション1、 UnlockErrorNoLogin] 'または' [action1、UnlockErrorNeedsSudo] 'を実行します。つまり、 'data Actions = Action1 | ActionIfNoLogin | ActionIfNeedsSudo'私は、関数の実行に依存するアクションのリストを収集したいと思います。しかし、「行動1」はモナドではない。 – cpa
@cpaモナドアクションが実際に上記のエラーの1つに到達するのに十分に実行された場合、本当に 'action1'アクションが実行されます。 –
私のOPはあまり明確ではありませんでしたが(私はそれを更新しました)、アクションを実行するのではなく、実行するアクションのリストを返します。良い夜の眠りの後、私は実際には無料のモナドを再開発していると思う。 – cpa