簡単な定義
t m
これを機能させるには、モナドのインスタンスが 必要です。
import Control.Monad
import Control.Monad.Trans
co :: Monad m => m a -> m a -> m a
co = undefined
co' :: (MonadTrans t, Monad m, Monad (t m)) => t m a -> m a -> t m a
co' one two = lift . flip co two . return =<< one
問題に対する別の見方
lift
パッケージからの定義を見ると、transformers
似たようなものが欲しいので、答えを見つけるのに役立ちます
lift . return = return
lift (m >>= f) = lift m >>= (lift . f)
あなたが持っていてflip co one :: Monad m => m a -> m a
、それを持ち上げて type の関数を取得したいのと同じように(MonadTrans t, Monad m, Monad (t m)) => t m a -> t m a
。だからリフトの足跡をたどって
lift' :: (MonadTrans t, Monad m, Monad (t m)) => (m a -> m a) -> t m a -> t m a
lift' f tma = tma >>= (lift . f . return)
定義co'
は簡単です
co' one two = lift' (flip co two) one
問題
上記のソリューションは型を満たしているだけですが、セマンティクスも満たしていますか? 問題を確認するためco
に、最初のアクションを見ずに常に 2 番目のアクションを返す関数を考えてみましょう。上記co'
は、何かを決定する前に常に最初のアクションを実行するため、これを行うことはできません。したがって、最初のアクションの副作用は、 では発生しco'
ませんが、 で発生しco
ます。
一般解は存在するか?
ないと思います。実際にその関数を一般的に実装したいのは、 のような関数であり、実際に の副作用を実行せずにアクションt m a -> (m a -> m b) -> t m b
を取得する必要があるためです。m a
t m a
m a
したがって、m
がIO
モナドであり、t
が何らかの状態を持つ State トランスフォーマーであり、one
アクションが実際にミサイルを発射し、その成功または失敗に応じて が変更されるとしState
ます。ここで、ミサイルを発射せずに状態を実際に変更する必要があります。一般に、それは不可能です。
co
どのアクションが最初に実行されるかなどの情報がわかっている場合はco'
、上記の方法を使用して実装できます。