2

私は、チュートリアルのWrite Yourself A Scheme をフォローして拡張してきました。LispValモナドトランスフォーマーのいくつかのレイヤーにラップされた型があります。

import qualified Data.Map as M

data LispVal   = ...
data LispError = ...

type Bindings = M.Map String (IORef LispVal)
data Env = Environment { parent :: Env, bindings :: IORef Bindings }

type IOThrowsError = ErrorT LispError IO
type EvalM = ReaderT Env IOThrowsError

using のアイデアReaderTは、エバリュエーターを介して (変数バインディングを維持する) 環境を自動的に渡すことができるということaskです。これは、追加のパラメーターとして環境を明示的に渡すよりも望ましいようです。ContT継続を実装するときは、モナド変換子で同様のトリックを行い、継続のための余分な引数を渡さないようにしたいと思います。

ただし、これを行うことで環境を変更する方法がわかりません。たとえば、新しい変数を定義したり、古い変数の値を設定したりします。

it具体的な例として、if ステートメントを評価するたびに、変数をテスト句の結果にバインドしたいとします。私の最初の考えは、環境を直接変更することでした:

evalIf :: [LispVal] -> EvalM LispVal
evalIf [test, consequent, alternate] = do
  result <- eval test
  bind "it" result
  case (truthVal result) of
    True  -> eval consequent
    False -> eval alternate

aを anytruthValに代入する関数を次に示します。しかし、環境を変更するように関数を記述する方法がわかりません。BoolLispValbind

私の2番目の考えは、使用することでしたlocal:

evalIf :: [LispVal] -> EvalM LispVal
evalIf [test, consequent, alternate] = do
  result <- eval test
  local (bind "it" result) $ case (truthVal result) of
    True  -> eval consequent
    False -> eval alternate

しかし、ここでbindは type が必要でEnv -> Envあり、環境内で s を値として使用してIORefいるため、署名付きの関数しか記述できませんEnv -> IO Env

これは可能ですかStateT、代わりに使用する必要がありReaderTますか?

4

2 に答える 2

7

ReaderT範囲指定された読み取り専用環境がある場合に使用されます。例えば、通訳者。環境はReaderTレキシカルにネストされるため、バインディングに遭遇するたびに環境を拡張できます。そのようです:

eval (LetE x e1 e2) = do
    env <- ask
    v   <- eval e1
    local (M.insert x v) (eval e2)

これは私の古い投稿で、いくつかの例があります

IORefsそのような環境がある場合は、動的スコープで面白いゲームをプレイしている場合を除き、も使用する必要はないはずです? IORefs の理由は何ですか?

于 2012-05-16T13:38:53.297 に答える
3

変更可能なコンテキストStateTが必要な場合は、 ではなく が必要ですReaderT。Reader は のみを許可しasktell( のようにWriterT) は許可しません。State は Reader と Writer の組み合わせであり、 、 、 を自由に実行getできputますmodify

于 2012-05-16T13:39:12.700 に答える