Freer Effectsが、だいたいわかった: 14 Freer Effectsで、IOモナドなどの既存のモナドを使用する
はじめに
Freer Effectsでは、型リストの要素であるモナドの効果を、ひとつずつ処理していく。しかし、IOやSTなどのモナドは、そのようなやりかたでは処理できない。IOなどのモナドは「そのまま」処理系にわたす必要がある。
また、Freer Effectsのわくぐみ以外の、たとえばモナド変換子といったわくぐみを使ったモナドについても、Freer Effectsの仕組みでは処理できない。
そのようなFreer Effectsの範囲外にあるモナドを利用するには、これらのモナドを型リストの最後の要素として残しておき、「そのまま」とりだすような仕組みが必要になる。
目次
(0). 導入
-
Freeモナドの概要
- Freeモナドとは
- FreeモナドでReaderモナド、Writerモナドを構成する
- 存在型(ExistentialQuantification拡張)の解説
- 型シノニム族(TypeFamilies拡張)の解説
- データ族(TypeFamilies拡張)の解説
- 一般化代数データ型(GADTs拡張)の解説
- ランクN多相(RankNTypes拡張)の解説
-
FreeモナドとCoyoneda
- Coyonedaを使ってみる
- FreeモナドとCoyonedaを組み合わせる
- いろいろなモナドを構成する
-
Freerモナド(Operationalモナド)でいろいろなモナドを構成する
- FreeモナドとCoyonedaをまとめて、Freerモナドとする
- Readerモナド
- Writerモナド
- 状態モナド
- エラーモナド
-
モナドを混ぜ合わせる(閉じた型で)
- Freerモナドで、状態モナドとエラーモナドを混ぜ合わせる
- 両方のモナドを一度に処理する
- それぞれのモナドを、それぞれに処理する
- Freerモナドで、状態モナドとエラーモナドを混ぜ合わせる
- 存在型による拡張可能なデータ構造(Open Union)
- 追加の言語拡張
- Open Unionを型によって安全にする
-
モナドを混ぜ合わせる(開いた型で)
- FreeモナドとOpen Unionを組み合わせる
- 状態モナドにエラーモナドを追加する
- Freer Effectsで、IOモナドなどの既存のモナドを使用する
- 左結合による効率の低下
- 型合わせした並び (Type aligned sequence)
- 「型合わせした並び」で「左結合による効率の低下」をふせぐ
- いろいろなEffect
- 関数send, handleRelayなどを作成する
- NonDetについてなど
- Trace
- Fresh, Cut
- Coroutine
開かれた直和型から最後の効果を取り出す
extract :: Union '[t] a -> t a
extract (Union _ tx) = unsafeCoerce tx
公開されたAPIを使って、まともに構成されたUnion型の値であれば、定義中の(Union _ tx)で_(ワイルドカード)がマッチする値は0であるはずだ。なので、unsafeCoerceはその値を確認しなくても安全に適用できる。モジュールOpenUnionのエクスポートリストに関数extractを追加しておく。
モジュールEffのOpenUnionを導入するimport文のインポートリストに関数extractを追加したうえで、関数runMを定義する。
runM :: Monad m => Eff '[m] a -> m a
runM (Pure x) = return x
runM (u `Bind` q) = runM . q =<< extract u
関数extractを適用して「最後に残った効果」を取り出している。取り出されたモナドがかえす値をバインド演算子で、qにあたえ、その結果にさらに関数runMを適用することで、Eff '[m] aをm aに変換している。
StateモナドとIOモナドとを混ぜ合わせる例
モジュールSampleに「StateモナドとIOモナドとを混ぜ合わせる例」を追加する。
runMSample :: (Member (State Integer) effs, Member IO effs) => Eff effs ()
runMSample = do
(a :: INteger) <- get
inj (print a) `Bind` Pure
この形は何度も出てきているが、Bind (inj m) Pure
とすることで、モナドmをEff effs aのかたちに変換することができる。ここではmがprint aだということ。試してみよう。
*Samples> runM $ runMSample `runState` 123
123
((),123)
print a
によって取り出した状態が表示されている。
まとめ
最後に残った効果がモナドであったときに、それをそのまま取り出す関数runMを定義した。これを使えば、Freer Effectsのわくぐみの外で定義されたモナドをFreer Effectsで定義された効果と混ぜて使うことができる。ただし、そのような生(なま)のモナドは型リストの末尾に置かれ、「最後に取り出す」ことしかできず、また複数指定することもできない。