作用とは
この記事ではEff
型の第一型引数を指します。
拡大とは
この記事において、作用A
が作用B
より大きいとは
このようなA ⊂ B
を指し、 「拡大」は、
のような((X :>)
を補填するような)操作を指します。 (一般的になんと言うのかは知らない)
問題
この問題についてのおおよその背景はみょんさんの記事を見た方がとてもわかりやすいと思いますが
今回の焦点は、extensbile-effectsパッケージにおいてこのような2つの型があった時に
type PureComputation = Eff (Exc () :> Void)
type ImpureComputation = Eff (Exc () :> Lift IO :> Void)
作用が小さいものを大きいものに拡大する一般的な関数が提供されていないということです。
直感的には、純粋な計算(PureComputation
)というのは不純な計算(ImpureComputation
)を含んでも問題がない気がします。 (でも、それを実現する、より一般的な方法がexntensible-effectsでは用意されていない)
解決(自分で作る)
なのでこんな関数を定義します。 (Thanks @as-capbabl!)
upgrade :: (Functor t, Typeable t) => Eff r a -> Eff (t :> r) a
upgrade = fromView . go . toView
where
go (Impure uni) = Impure $ weaken (upgrade <$> uni)
go (Pure x) = Pure x
こんな感じで使います。
{-# LANGUAGE TypeOperators #-}
import Control.Eff (Eff, (:>), run)
import Control.Eff.Exception (Exc, runExc, liftEither)
import Control.Eff.Lift (Lift)
import Control.Monad.Free.Reflection (FreeView(..), fromView, toView)
import Data.OpenUnion (weaken)
import Data.Typeable (Typeable)
import Data.Void (Void)
type PureComputation = Eff (Exc () :> Void)
type ImpureComputation = Eff (Lift IO :> Exc () :> Void)
-- 何らかの純粋な計算
pureCompute :: PureComputation ()
pureCompute = return ()
-- 何らかの不純な計算
impureCompute :: ImpureComputation ()
impureCompute = return ()
-- 不純なものに拡大された純粋な計算
impureCompute' :: ImpureComputation ()
impureCompute' = upgrade pureCompute
余談
extensibleのEffectモジュール にはcastEff :: IncludeAssoc ys xs => Eff xs a -> Eff ys a
という関数が予め用意されています。
この記事はこちらから修正リクエストを送ることができます。
extensible-effectsの作用を拡大方向に変換する - github
ゴミ箱ボタンの左にある、鉛筆ボタンを押してね!