4

私は48時間で驚くべきWriteYourselfa Schemeに取り組んでおり、コアタスクを完了し、それを拡張したいと思っていましたが、問題が発生しました。私がやりたかったのは、eval関数をランタイムで使用できるようにすることですが、それをグローバル環境に保存する際に問題があります。

ランタイム環境のタイプは次のとおりです。

type Env = IORef [(String, IORef LispVal)]

Haskellのeval実装は次のタイプです。

eval :: Env -> LispVal -> IOThrowsError LispVal

グローバル環境は、次のタイプのマッピングです。

primitiveBindings :: IO Env

純粋関数と混合されたIOを実行する関数が含まれているためです。私の試みは、次のようなグローバル環境で部分的に適用されevalたホストにランタイムを設定することでした。eval

baseFun :: [(String, [LispVal] -> IOThrowsError LispVal)]
baseFun = [("eval", unaryOp (eval (liftIO $ readIORef primitiveBindings)))]

どこunaryOpにありますか:

unaryOp :: (LispVal -> ThrowsError LispVal) -> [LispVal] -> ThrowsError LispVal
unaryOp f [v] = f v

次に、要素をグローバル環境に追加したかったのですが、次のコンパイルエラーが発生します。

Couldn't match expected type `IORef a'
       against inferred type `IO Env'
In the first argument of `readIORef', namely `primitiveBindings'
In the second argument of `($)', namely `readIORef primitiveBindings'
In the first argument of `eval', namely
    `(liftIO $ readIORef primitiveBindings)'

このパターンはreadIORef envコードで頻繁に発生するように見えるため、ここで機能しない理由は不明です。私の過ちについての啓蒙を大いに感謝します。参考までに、私のコードは、参照としての元のチュートリアルの最終的なコードとほぼ同じです。

ありがとうございました

4

4 に答える 4

4

私が知る限り、これはグローバル環境でprimitiveBindingsなく、実行されると、すでに設定されているプリミティブバインディングを使用して新しいマッピングを生成するアクションです。おそらくそれのより良い名前はだっただろうcreatePrimitiveBindings。ハックスなしでHaskellに適切なグローバルを持つ方法はありませんunsafePerformIO

したがって、追加しようとしている方法では、アクションを再実行しているため、新しいeval環境ですべてが評価されます。それが本当にあなたが意図したものであるならば、私たちはタイプエラーを簡単に処理することができます:primitiveBindings

baseFun :: [(String, [LispVal] -> IOThrowsError LispVal)]
baseFun = [("eval", evalInNewEnv)]
    where evalInNewEnv args = do
              env <- liftIO primitiveBindings
              unaryOp (eval env) args

readIORefご覧のとおり、ここでは何も必要ありません。これは、。の型の同義語をevalすでに期待しているためです。EnvIORef

ただし、「グローバル」な環境で働きたいと思うのですがeval、その場合は、先ほど申し上げたようにグローバルがないので、どういうわけかこの環境を渡してもらう必要があります。たとえば、このようなものを定義して、その中に新しい環境を作成することができますeval

primitiveBindingsWithEval :: IO Env
primitiveBindingsWithEval = do
    env <- primitiveBindings    
    bindVars env [("eval", unaryOp (eval env))]

primitiveBindingsその後、新しい環境が必要なprimitiveBindingsWithEval場合は、の既存の使用法をに置き換えることができますeval

于 2012-05-12T01:36:01.947 に答える
3

あなたの問題(エラーメッセージから)はreadIORef primitiveBindingsです。

readIORef :: IORef a -> IO aprimitiveBindings :: IO (IORef [(String, IORef LispVal)])タイプが並んでいないので(通知primitiveBindingsはで囲まれていIOます)。

試してみてくださいprimitiveBindings >>= readIORef

タイトルの質問に対して:「IOから要素を取り出す」ことはできません。これIOは世界であり、haskellはその実装を公開しないため、Monad(bind and return)によって提供されるインターフェイスのみが残ります。 。必要に応じて、誰かがその点のより良い説明にあなたを導くことができます。

于 2012-05-10T00:26:15.137 に答える
2

仮定:タイプはunaryOp実際にIOThrowsErrorあなたが書いたすべての場所に関係しますThrowsError。次に、ドメインを知らなくても、指定したタイプを確認するだけで、意味を推測できます。

baseFun = [("eval", unaryOp (\lispVal -> liftIO primitiveBindings >>= flip eval lispVal))]

それはあなたが望んでいたものにかなり近いように見えますか?

于 2012-05-10T00:22:47.120 に答える
2

あなたは混合IOしていて、純粋関数はやや無計画に機能しています。基本的に、3つの問題があります。

  1. unaryOp (eval (liftIO $ readIORef primitiveBindings))タイプが必要ですが、タイプ[LispVal] -> IOThrowsError LispValunaryOp返します。これは。と同じではありませ[LispVal] -> ThrowsError LispValん。これを修正する方法はいくつかありますが、はるかに簡単なのは、次のタイプを一般化することです。IOThrowsErrorThrowsErrorunaryOp

    unaryOp :: (a -> b) -> [a] -> b
    unaryOp f [v] = f v
    

    (おそらく、2番目の引数がシングルトンリストでない場合のより良いエラーメッセージを思い付くはずです。)

    unaryOpは以前と同じことをし[LispVal] -> IOThrowsError LispValますが、あなたがそれを与えると戻ることもできます。それはまさにそれLispVal -> IOThrowsError LispVal与えるものです。ここまでは順調ですね。だが:eval

  2. liftIO (readIORef primitiveBindings)はタイプチェックを行いません。これは、でprimitiveBindingsはないため、実行時にを生成IORefするIOアクションです。したがって、 withを読み取るアクションを作成できます。つまり、実際には、その内容ではなく、自身をに渡したいので、それを実行したくありません。したがって、代わりに使用する必要があります。だが:IORefIORef=<<readIORef =<< primitiveBindingsIORefevalliftIO primitiveBindings

  3. evalタイプの引数を取りますが、EnvタイプliftIO primitiveBindingsですIOThrowsError Env(実際には、それよりも一般的ですが、十分に近いです)。したがって、最初に値を使用>>=またはdo抽出する必要があります。

    evalOp val = do
      env <- liftIO primitiveBindings
      eval env val
    
    baseFun = [("eval", unaryOp evalOp)]
    

余談ですが、質問のタイトルのトーンに対処するために:Haskellでは、から何かを得ることはありませんIO。あなたがしていることは、あなたがより多くを引き出すためにあなたがそれから入れる関数を書くことです 。(つまり表記法)そして、値を操作するための「リフティング」関数の通常の手法です。IOIO>>=doreturnIO

于 2012-05-12T17:56:27.683 に答える