82

STモナドはIOの弟のようなものであり、IOはRealWorld魔法が追加された状態モナドであると理解しています。私は状態を描くことができ、RealWorldがどういうわけかIOに入れられていることを描くことができますが、STモナドの型署名を書くたびに混乱しSTます。s

たとえば、ST s (STArray s a b)sそこでの仕事はどうですか?状態モナドの状態のように参照することができずに、計算間に人工的なデータ依存関係を構築するために使用されているだけforallですか?

私はただアイデアを捨てているだけで、私よりも知識のある人に説明していただければ幸いです。

4

3 に答える 3

79

モナド内のsオブジェクトがSTモナドの外側に漏れないようにしSTます。

-- This is an error... but let's pretend for a moment...
let a = runST $ newSTRef (15 :: Int)
    b = runST $ writeSTRef a 20
    c = runST $ readSTRef a
in b `seq` c

STRefさて、これは型エラーです(これは良いことです!元の計算の外に漏れたくないです!)。余分なため、タイプエラーですsrunST署名があることを忘れないでください:

runST :: (forall s . ST s a) -> a

これは、s実行している計算に制約がないことを意味します。したがって、評価しようとするとa

a = runST (newSTRef (15 :: Int) :: forall s. ST s (STRef s Int))

結果はタイプがなりますが、はinの外側に「エスケープ」されてSTRef s Intいるため、これは誤りです。型変数は常にの内側に現れる必要があり、Haskellはどこでも暗黙の数量詞を許可します。のリターンタイプを意味のある形で把握できるルールはありません。sforallrunSTforallforalla

別の例forall物事をエスケープできない理由を明確に示すために、次forallの簡単な例を示します。

f :: (forall a. [a] -> b) -> Bool -> b
f g flag =
  if flag
  then g "abcd"
  else g [1,2]

> :t f length
f length :: Bool -> Int

> :t f id
-- error --

もちろんf id、エラーです。ブール値がtrueかfalseかCharに応じて、のリストまたはリストが返されるためです。Intの例のように、それは単に間違っていSTます。

一方、 typeパラメーターがない場合はs、コードが明らかにかなり偽物であっても、すべてが正常に型チェックされます。

STの実際の動作:実装に関しては、STモナドは実際にはモナドと同じですが、IOインターフェイスが少し異なります。STあなたが実際に得るモナドunsafePerformIOまたは同等のものを舞台裏で使用するとき。これを安全に実行できる理由は、ST関連するすべての関数、特に。を含む部分の型アノテーションのためforallです。

于 2012-09-18T00:11:34.673 に答える
28

これsは、型システムが安全でないことをやめさせるハックにすぎません。実行時には何も「実行」しません。タイプチェッカーが疑わしいことをするプログラムを拒否するだけです。(いわゆるファントムタイプで、タイプチェッカーの頭にしか存在しないもので、実行時には何の影響もありません。)

于 2012-09-18T09:18:15.120 に答える
0

たとえば、次のように考えてみましょう。そこでの作業ST s (STArray s a b)はどのように行われるのでしょうか。s

GHCiセッションの時間:

GHCi, version 8.4.3: http://www.haskell.org/ghc/  :? for help
Prelude> let _ = (\x -> x^4) 5 in x

<interactive>:2:27: error: Variable not in scope: x
Prelude> 

ローカル変数スコープ(または参照フレーム) はラムダ/無名関数です-そのスコープ外では、それ以外の場合は未定義であるため、エラーが発生します。 x\x -> x^4x

のタイプrunST

runST :: (forall s . ST s a) -> a

同様の方法で機能します。ローカルで定量化された型変数のスコープsはタイプST s arunSTのパラメーターのタイプ)sです。結果のタイプに表示される場合、それもスコープ外であり、適切な定義がないため、タイプエラー。

したがって、次の場合:

  • アクションがありますm :: ST s (STArray s a b)

  • そして、あなたはうっかりしてから可変配列を抽出しようとしますm

      … (let arr = runST m in …) …
    

...その範囲外の脱出:s

forall s . ST s (STArray s a)

(および型システムによるその検出)は、特に並列処理と並行性のコンテキストで、とらえどころのないエラーのよく知られた原因である可変状態の共有を停止します。

于 2021-08-06T09:39:37.320 に答える