2017-11-19 8 views
4

なぜSTは次のコードを許可しないように設計されていますas shown in the wikibook?この記事では、STのエフェクトが別のSTに漏れることが示唆されていますが、なぜそれは本当に理解できません。ST参照は透過的ですか?

私は特定のST s (STRef s a)を実行できないようです。 STエフェクトは含まれていますが、参照は透過的ですが、そのような使用は無効と見なされますか?

直観は1 IO、多くの独立したSTがあることに関連IOSTの意味の違いがあることを私に語ったが、私はよく分かりません。

> runST (newSTRef True) 

<interactive>:5:8: error: 
    • Couldn't match type ‘a’ with ‘STRef s Bool’ 
     because type variable ‘s’ would escape its scope 
     This (rigid, skolem) type variable is bound by 
     a type expected by the context: 
      forall s. ST s a 
     at <interactive>:5:1-21 
     Expected type: ST s a 
     Actual type: ST s (STRef s Bool) 
    • In the first argument of ‘runST’, namely ‘(newSTRef True)’ 
     In the expression: runST (newSTRef True) 
     In an equation for ‘it’: it = runST (newSTRef True) 
    • Relevant bindings include it :: a (bound at <interactive>:5:1) 

> :t newSTRef 
newSTRef :: a -> ST s (STRef s a) 

> :t runST 
runST :: (forall s. ST s a) -> a 
+0

「ST」の私の理解は、それは宇宙のように考える。すべての「ST」操作は、ある宇宙を別の宇宙に変えるかもしれない(突然変異による)。そのため、 'ST'は' RealWorld'でタグ付けすることで 'IO'に変えることができます。だからあなたの質問は本当に 'IO'が純粋かどうかを尋ねるのと同じです。 – HuStmpHrrr

+4

'forall sのタイプのすべての値。 'IO A '型のすべての値は、(そのような値は値そのものではなく結果の値をどのように生成するかを表す計算であるため)参照透過的です。 'ST s(STRef s A)'は '実行 'できませんが、有効な計算です。例えば'STRef s A - > ST s B'型の関数を適用して' forall s 'を得ることができます。 ST s B 'を実行することができる。しかし、私は実際に何が問題なのか、なぜ問題のコードが許されるべきだと思いますか? (注:「私は本当になぜか分かりません」という説明はありません) – user2407038

+1

'ST'はダイナミックに割り当てられたメモリとそのメモリへのポインタ/参照を許可します。原則として、すべてのメモリを 'runST'の終わりに解放できるように設計されています。そのためには、ポインタが 'runST'をエスケープできないことが不可欠です。このようなエスケープされたポインタはまた、評価順序付けに関連する奇妙な効果を引き起こす。 'f(runST $ writeSTRef r True)+ g(runST $ readSTRef r)'は、どのrunSTが最初に評価されるかによって、異なる結果を評価することができます。参照透明性は、このような注文問題を回避するためのものです。 – chi

答えて

7

もっと質問自体よりも、質問のタイトルへの答えが、それでも:

はい、STは参照透明です。これは単なる推測と信じられていた長い間

、そして唯一の今年、私たちはそのための適切な証拠を持っている:

州の単項カプセル化のための論理的関係:runSTの存在下で、文脈等価性を証明
アミンTimany、レオStefanesco、モーテン・クロ-Jespersen、ラースBirkedal
条件付きで、私は本当にunderstaない2018
http://iris-project.org/Preprint PDF

6

をPOPLする受け入れあなたが求めているものは何ですか、私はあなたの質問ふりをするつもりです:

なぜrunST (newSTRef True)を禁止するように設計されているのですか?

ことが許可された場合、それはもはや参照透明ないだろう:

main = do 
    let r = runST (newSTRef True) 
    evaluate (runST (writeSTRef r False)) 
    print (runST (readSTRef r)) 

このコードは考え出力False

main = do 
    evaluate (runST (writeSTRef (runST (newSTRef True)) False)) 
    print (runST (readSTRef (runST (newSTRef True)))) 

このコード(ユーザ毎にrをインライン化した結果)が出力True、希望runST (newSTRef True)の各インスタンスは、新規STRef変数を割り当てるからです。

実際にrunSTの方法が実装されているため、ST sアクション内に割り当てられたすべてのリソースがそのアクションをエスケープできません。

IOとの違いは、runIOの機能がないということです。複数の独立したIOアクションを作成することはできますが、runIO(それらは単なる値)なしでは関係ありません。実際にIOアクションを実行する唯一の方法は、mainにバインドし、ランタイムシステムにそれを拾い上げさせることです(暗黙的なrunIOmainにラップされているようなものです)。IOというアクションが1つしかありません。プログラム。

もちろん、この単一のアクションは複数の小さなアクションを組み合わせることで構成できますが、最終的にはまだモノリスです。一方、runSTは、複数のアクションを独立して実行できるようにします。

+0

私の質問は、私が予想していたよりも複雑なものに触れることに気付きました。私はそれが不明であることを認めます。しかし、この答えは私に(そしてうまくいけば他人に)いくつかの手がかりを与えます。最初は、 '' s''が純粋なコードで実際の参照を混ぜるので、 '' s = ''が '' STRef s x'のときに '' ST s a''が参照透過性を壊すと間違って仮定しました!しかし、実存型は 'runST'の正確さを強制する上で決定的に重要であると思われます。この部分は私が理解することが最も難しいと思っており、それは非常に巧妙な発明(または発見)であるように感じます。 – sevo

関連する問題