「48時間でSchemeを書こう/変数と代入」の版間の差分
削除された内容 追加された内容
編集の要約なし |
編集の要約なし |
||
1 行
にも関わらず、Haskellでは同じ状態をシミュレートする方法がいくつか
残念なことに、このStateモナドは私たちのためには上手く動いてはくれません。というのも、私たちが格納しておきたいデータの型というのは、かなり複雑だからです。もっともシンプルな環境では、変数名から値にマッピングするのを格納することによって、[(String, LispVal)]でやり通すことができますが、しかし、私たちが関数の呼び出しを扱い始めると、これらのマッピングは、任意の深さの、入れ子になった環境のスタックになるでしょう。そして、私たちがクロージャーを追加したとき、この環境は環境はきっと任意の関数の値に保存され、そしてプログラム全体から返されるかもしれません。事実、
代わりに、私たちは''State スレッド''と呼ばれるものを使って、私たちの為に、この集合状態をHaskellに管理させましょう。これは変数を得たり、設定する為の機能を使って、他のプログラム言語と同じように、可変変数を扱うことができるようになります。Stateスレッドには二つの種類があります。the [http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Monad-ST.html ST monad] は残りのプログラムへ状態を回避させることなしに、単体で実行されるような、ステートフルな
import Data.IORef
13 行
type Env = IORef [(String, IORef LispVal)]
これで、変化が多いLispValを文字列として対応した、IORefが持つリストとして、Envを定義できます。リストかそれ自身両方のための、そして個々の値のために、IO Refsが必要です。というのも、
IORefs は ただ、IOモナドの内部の中でのみ使うことができます。
nullEnv :: IO Env
nullEnv = newIORef []
ここから、ことはちょっとだけ複雑になります。というのも同時に''二つ''のモナドをあつかないといけないからです。覚えておいてください。
多様なモナドが持つ機能を結合させるような、モナド変化として知られるようなメカニズムを、Haskellは供給しています。私たちは、きっとその一つを使うことでしょう。 - [http://www.haskell.org/ghc/docs/6.4/html/libraries/mtl/Control.Monad.Error.html#t%3aErrorT ErrorT] - これは、IOモナドのトップで、エラーハンドリングの機能性があるレイヤーを与えます。私たちの最初のステップは、私たちの結合したモナドのために、別の名前の型を作ってあげます。
28 行
ThrowsErrorやIOThrowsErrorのようなものは、実際にタイプのコンストラクタなのです。というのも、私たちは最後の引数や関数の返り値のタイプへと入れるからです。しかし、ErrorTは、まっさらな、古い、おのおのの引数から、一つ以上の引数を取ります。私たちは、自分たちのエラーを扱う機能のレイヤーにある、モナドの型を明示しなければならないのです。なので、LispErrorを投げるIOの行動を含めることができるモナドを作ります。
私たちは、IOThrowsErrorとThrowsErrorのミックスがあります。しかし違う型によるアクションは同じdoブロックの中に含めることが出来ないのです。それが、たとえ本質的には同じ機能を提供していた、としても。Haskellは、低い型(IO)の値を、結合したモナドへ送るためのメカニズムが、既に提供されています。 [http://www.haskell.org/all_about_monads/html/transformers.html#lifting lifting]です。残念なことに、まだ変形していない、上位型の値を
liftThrows :: ThrowsError a -> IOThrowsError a
58 行
(lookup var env)
これは、前出した関数のように、IORefから実際の環境を取り出し始めます。しかし、getVarはIOthrowsErrorを使います。というのも、これも同様にいくつかのエラーを扱う必要があるからです。結局
関数に、値をセットする関数を作りましょう。
|