extensible-effectsの作用を拡大方向に変換する

作用とは

 この記事ではEff型の第一型引数を指します。

-- Fooは作用
Eff Foo a

拡大とは

 この記事において、作用Aが作用Bより大きいとは

type A = X :> Y :> Z
type B = Y :> Z

このようなA ⊂ Bを指し、 「拡大」は、

Eff B a -> Eff A a

のような((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 という関数が予め用意されています。

筆者プロフィール

my-latest-logo

aiya000(あいや)

せつラボ 〜圏論の基本〜」 「せつラボ2~雲と天使と関手圏~」 「矢澤にこ先輩といっしょに代数!」を書いています!

強い静的型付けとテストを用いて、バグを防ぐのが好き。Haskell・TypeScript。