SYB (または他の Haskell ジェネリック パッケージ)を使用して、サブ計算の環境を変更するためにReader使用するモナドに変換を作成するにはどうすればよいですか? and (with ) のlocal型は、サブ計算をラップする(of type )の使用をサポートしていないようです。可能であれば、「標準」/「既製品」の変換を使用するソリューションを希望します。GenericMeverywhereMa -> m alocalm 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し、ReaderSYB を使用して定型文の再帰ケースを自動的に処理することです(:@)。
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 ExpVar - ケースは
Lam再帰の前に環境を変更します:ここでeverywhereMは機能しません(私が知る限り)。Binder型は をどこで使用する必要があるかを示しているため、特定のケースlocalに適用することを望むかもしれませんが、その方法がわかりません。mkMBinder Exp -> Reader Int (Binder Exp)
上記のコードを含む、より多くの例を含む Gist を次に示します。