※ 本記事はextensible-effectsパッケージではなくextensibleパッケージの
Data.Extensible.Effectモジュールについての記事です
解答
context :: ( Associate "greet" (WriterEff [String]) xs
, Associate "message" (WriterEff [String]) xs
) => Eff xs ()
上記の型を具体化すると
context :: Eff '["greet" >: WriterEff [String], "message" >: WriterEff [String]] ()
という具体型になる。
Data.Extensible.Effectでの抽象的なEff型
extensible-effectsではこのような抽象化されたEff型で、Writerモナド相当の操作ができましたが
context :: ( Member (Writer [String]) r
) => Eff r ()
context = do
tell ["hi"]
tell ["thanks !"]
extensibleパッケージでは、WriterEff, ReaderEffなどの
Data.Extensible.Effectでの作用を名前付き(Symbol付き※1)で管理できるので、
重複した作用を別のものとして扱うことができる。
context :: ( Associate "greet" (WriterEff [String]) xs
, Associate "message" (WriterEff [String]) xs
) => Eff xs ()
context = do
tellEff #greet ["hi"]
tellEff #message ["extensible, OverloadedLabels, and TypeApplications is great"]
tellEff #greet ["thanks !"]
-- これらを
-- leaveEff . runWriterEff @ "message" . runWriterEff @ "greet"
-- すると
-- (((),["hi","thanks !"]),["extensible, OverloadedLabels, and TypeApplications is great"])
-- という値が得られる
- 参考になる公式記事
※1
String([Char])を種に昇格するとSymbolになるっぽい? まだ調べてない。
Data.Extensible.Effectでの具体的なEff型
上の型
context :: ( Associate "greet" (WriterEff [String]) xs
, Associate "message" (WriterEff [String]) xs
) => Eff xs ()
を解凍する関数
leaveEff . runWriterEff @ "message" . runWriterEff @ "greet"
の型をGHCのtyped hole機能で調べてみると
(Monoid w, Monoid w1)
=> Eff '["greet" >: WriterEff w, "message" >: WriterEff w1] a
-> ((a, w), w1)
と出る。
ちょうど我々がそこに当てはめているMonoidは[String]なので、
leaveEff . runWriterEff @ "message" . runWriterEff @ "greet"
:: Eff '["greet" >: WriterEff [String], "message" >: WriterEff [String]] ()
-> (((), [String]), [String])
である。
なので
context :: Eff '["greet" >: WriterEff [String], "message" >: WriterEff [String]] ()
である。
余談
#fooというのはOverloadedLabels拡張の機能で、@ BarというのはTypeApplications拡張の機能。