4

サンプルコードを見つけて、少し変更しました

counter = unsafePerform $ newIORef 0

newNode _ = unsafePerformIO $
              do
                i <- readIORef counter
                writeIORef counter (i+1)
                return i

実行されるたびに、1、2、3、3 などを返します。

しかし、私はそれを

newNode = unsafePerformIO $
              do
                i <- readIORef counter
                writeIORef counter (i+1)
                return i

その後、実行するたびに0になります。

なぜこれが起こっているのですか?それを修正するにはどうすればよいですか?

4

3 に答える 3

10

2番目のバージョンnewNodeでは、関数ではなく単純な値です。したがって、haskell はそれを 1 回だけ評価し、アクセスするたびにその評価の結果を返しますnewNode

警告の言葉:unsafePerformIO参照透過であることがわかっている IO アクション以外での使用は危険です。一部の最適化との相互作用が悪く、通常は期待どおりに動作しない可能性があります。その名前に「安全でない」という言葉が含まれているのには理由があります。

コードをいじる方法としてunsafePerformIOは問題ありませんが、実際のコードでそのようなものを使用したい場合は、再検討することを強くお勧めします.

于 2010-12-16T14:23:16.727 に答える
4

明確化と同じように、構築しているように見えるアプリケーションの場合、を使用してIORefを作成unsafePerformIOすることはごく普通のことであり、手続き型言語のグローバル変数にHaskellを最も近いものにすることができます。おそらく賢明ではないのは、あなたの行動にそれを使用することです。たとえば、newNodeはおそらく次のように書くのが最適です。

newNode = do i <- readIORef counter
             writeIORef counter (i+1)
             return i

そして、呼び出す予定の場所はnewNode、IOアクション自体である必要があります。

もう1つの詳細:を使用してこれを変更しない限り、デフォルトcounterでタイプが設定されます。次のようなジェネリック型を付けようとしないでください。そうすれば危険があります。IORef IntegerdefaultNum a => IORef a

于 2010-12-16T22:27:37.253 に答える
3

通常のプログラミングでは、このような構造を使用しないでください。コンパイラがさまざまな最適化を適用して、目的の効果を無効にしたり予測不能にしたりする可能性があるためです。可能であれば、State代わりにモナドのようなものを使用してください。それはよりクリーンで、常にあなたが望むように動作します。どうぞ:

-- This function lets you escape from the monad, it wraps the computation into a monad to make it, well, countable
counter :: (State Int x) -> x
counter = flip evalState 0

-- Increments the counter and returns the new counter
newNode :: State Int Int
newNode = modify (+1) >>= get

あなたの質問に対する答えは、何 sepp2k 悲しいかを参照してください。あなたが説明することは、グローバルに利用可能なもの(構成など)があり、変更される可能性が低く、ほぼどこからでも利用できる必要がある場合に特に役立ちます。純粋さの基本原則に反するので、賢明に使用してください。可能であれば、代わりにモナドを使用してください(私が説明したように)。

于 2010-12-16T14:44:47.017 に答える