SYB (または他の Haskell ジェネリック パッケージ)を使用して、サブ計算の環境を変更するためにReader
使用するモナドに変換を作成するにはどうすればよいですか? and (with ) のlocal
型は、サブ計算をラップする(of type )の使用をサポートしていないようです。可能であれば、「標準」/「既製品」の変換を使用するソリューションを希望します。GenericM
everywhereM
a -> m a
local
m a -> m a
代表例
(不思議な) 再帰的なデータ型:
{-# LANGUAGE DeriveDataTypeable , Rank2Types , ViewPatterns #-}
import Data.Generics
import Control.Applicative
import Control.Monad.Reader
import Control.Arrow
data Exp = Var Int | Exp :@ Exp | Lam (Binder Exp)
deriving (Eq , Show , Data , Typeable)
newtype Binder a = Binder a
deriving (Eq , Show , Data , Typeable)
埋め込まれたすべてのs を、それらをラップする sInt
の数よりも大きな値でインクリメントする再帰関数:Binder
-- Increment all free variables:
-- If G |- e:B then G,A |- weaken e:B.
weaken1 :: Exp -> Exp
weaken1 = w 0
where
w :: Int -> Exp -> Exp
-- Base case: use the environment ('i'):
w i (Var j) = wVar i j
-- Boilerplate recursive case:
w i (e1 :@ e2) = w i e1 :@ w i e2
-- Interesting recursive case: modify the environment:
w i (Lam (Binder e)) = Lam (Binder (w (succ i) e))
wVar :: Int -> Int -> Exp
wVar i j | i <= j = Var (succ j)
| otherwise = Var j
目標は、i
パラメータを環境に配置weaken1
し、Reader
SYB を使用して定型文の再帰ケースを自動的に処理することです(:@)
。
SYB ではなくweaken1
、環境を使用したの書き換え:Reader
weaken2 :: Exp -> Exp
weaken2 e = runReader (w e) 0
where
w :: Exp -> Reader Int Exp
w (Var j) = do
i <- ask
return $ wVar i j
w (e1 :@ e2) = (:@) <$> w e1 <*> w e2
w (Lam (Binder e)) = Lam . Binder <$> local succ (w e)
例のポイント:
- ケースは
(:@)
典型的なボイラープレート再帰です:everywhereM
ここでは自動的に機能します。 - ケースは環境を使用しますが、
Var
それを変更しません:ケースに固有のものにeverywhereM
適用することにより、ここで機能しmkM
ます。Exp -> Reader Int Exp
Var
- ケースは
Lam
再帰の前に環境を変更します:ここでeverywhereM
は機能しません(私が知る限り)。Binder
型は をどこで使用する必要があるかを示しているため、特定のケースlocal
に適用することを望むかもしれませんが、その方法がわかりません。mkM
Binder Exp -> Reader Int (Binder Exp)
上記のコードを含む、より多くの例を含む Gist を次に示します。