2012-07-26 5 views
13

私はタイプ[ST s (Int, [Int])]のために結合していると、私は次のようにマップを使用して各要素にrunSTを適用しようとしています:ハスケル:マップrunST

name :: [ST s (Int, [Int])] --Of Course there is a real value here 
map runST name 

これは私にエラーメッセージを表示します

Couldn't match expected type `forall s. ST s b0' 
    with actual type `ST s0 (Int, [Int])' 
Expected type: [forall s. ST s b0] 
    Actual type: [ST s0 (Int, [Int])] 
In the second argument of `map', namely `name' 
In the expression: map runST name 

私は何か誤解しているに違いありません。私はrunST and function compositionを認識していますが、これが当てはまるかどうかはわかりません。

よろしくお願いいたします。

答えて

14

runSTのステートトランスフォーマーを実行するたびに、他のすべてのステートトランスとは別のローカルステートで動作します。 runSTの新しい状態タイプを作成し、そのタイプの引数を呼び出します。 ST s1()ST s2()runSTによって作成されたいくつかの未知の種類s1s2のため:あなたは

let x = runST (return()) 
    y = runST (return()) 
in (x, y) 

を実行するのであれば、例えば、最初return()と第二return()は、さまざまな種類があります。

runSTには、状態タイプがsの引数を指定してコールしようとしています。これはrunSTが作成する州のタイプではなく、あなたが選択できる他のタイプもありません。 runSTを呼び出すには、の任意の状態タイプを持つことができる引数を渡す必要があります。次に例を示します。

r1 :: forall s. ST s() 
r1 = return() 

r1が多型であるため、その状態はrunSTによって選択されているものの種類を含む、任意の型を持つことができます。あなたは(-XImpredicativeTypes付き)多型r1 Sのリストの上にrunSTをマップすることができます。

map runST ([r1, r1] :: [forall t. ST t()]) 

しかし、あなたは非多型r1 Sのリストの上にrunSTをマッピングすることはできません。

map runST ([r1, r1] :: forall t. [ST t()]) -- Not polymorphic enough 

タイプforall t. [ST t()]は、すべてのリストの要素は、状態の種類tを持っていることを言います。しかし、それぞれにはrunSTが呼び出されるため、すべてが独立した状態タイプを持つ必要があります。それがエラーメッセージの意味です。

すべてのリスト要素に同じ状態を与えることができれば、次に示すようにrunSTを1回呼び出すことができます。明示的な型署名は必要ありません。

runST (sequence ([r1, r1] :: forall t. [ST t()])) 
+0

うわー、それは素晴らしいです!詳細な説明をありがとう。 –

6

nameは、多型ではありません。あなたの声明

name :: [ST s (Int, [Int])] 

手段 'ステートフルな計算のリストを返す(INT、[INT])を持っているとまったく同じs'。このタイプは、「sあなたは今までにを想像することができます何もすることができますステートフルな計算をとる関数」を意味

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

:しかしrunSTの種類を見てください。これらのタイプの計算は同じではありません。そして最後に:それが今よりも、あなたが見

map runST :: [forall s. ST s a] -> [a] 

は、あなたのリストには、それ以上の多型の値が含まれている必要があります。 sタイプはリストの各要素で異なる可能性がありますが、nameと同じタイプではない可能性があります。タイプシグニチャをnameに変更します。すべてがOKであるはずです。いくつかの拡張機能を有効にする必要があるかもしれませんが、GHCはあなたにどの拡張機能を伝えることができるはずです。

5

私はrunSTのタイプのための推論を説明してみましょう:

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

そして、なぜそれがこの単純なもののようではありません。このalternativeRunSTが働いていただろうと

alternativeRunST :: ST s a -> a 

注意をあなたのプログラムのために。

leakyVar :: STRef s Int 
leakyVar = alternativeRunST (newSTRef 0) 

evilFunction :: Int -> Int 
evilFunction x = 
    alternativeRunST $ do 
    val <- readSTRef leakyVar 
    writeSTRef leakyVar (val+1) 
    return (val + x) 

次にあなたがGHCiの中で行くことができるし、実行します:

alternativeRunSTも私たちはSTモナドの外に変数をリークすることができていただろう

>>> map evilFunction [7,7,7] 
[7,8,9] 

evilFunctionは参照透明ではありません!

ところで、ここではそれを自分で試してみることは、上記のコードを実行するために必要な「悪いST」のフレームワークです:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
import Control.Monad 
import Data.IORef 
import System.IO.Unsafe 
newtype ST s a = ST { unST :: IO a } deriving Monad 
newtype STRef s a = STRef { unSTRef :: IORef a } 
alternativeRunST :: ST s a -> a 
alternativeRunST = unsafePerformIO . unST 
newSTRef :: a -> ST s (STRef s a) 
newSTRef = ST . liftM STRef . newIORef 
readSTRef :: STRef s a -> ST s a 
readSTRef = ST . readIORef . unSTRef 
writeSTRef :: STRef s a -> a -> ST s() 
writeSTRef ref = ST . writeIORef (unSTRef ref) 

本当runSTは、私たちは、このような「悪」の機能を構築することはできません。それはどうですか?それはちょっとトリッキーだ、以下を参照:

を実行しようとすると:

>>> runST (newSTRef "Hi") 
error: 
    Couldn't match type `a' with `STRef s [Char]' 
... 

>>> :t runST 
runST :: (forall s. ST s a) -> a 
>>> :t newSTRef "Hi" 
newSTRef "Hi" :: ST s (STRef s [Char]) 

newSTRef "Hi"(forall s. ST s a)に適合していません。我々はまた、

dontEvenRunST :: forall a. (forall s. ST s a) -> Int 

を書くことができますし、それは我々としてforall a.を省略することと等価である

dontEvenRunST :: (forall s. ST s a) -> Int 
dontEvenRunST = const 0 

>>> dontEvenRunST (newSTRef "Hi") 
<interactive>:14:1: 
    Couldn't match type `a0' with `STRef s [Char]' 
     because type variable `s' would escape its scope 

注:また、GHCは私たちにかなりいいエラーを与えても、単純な例を使って見ることができるように前にした。 aの範囲はsよりも大きいが、newSTRef "Hi"の場合、その値はsに依存すべき

注意。タイプシステムはこれを許可しません。

+1

これは本当に便利だと分かった。ありがとう! –

+1

@yairchuここで説明されている方法で 'dontEvenRunST'で失敗する理由を詳しく教えてください:http://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types私が見たすべての記事では、それは状態引数と戻り値の型の間に型の不一致がありますが、戻り値の型が(Intのような)他の型であっても、型検査は行われません。 – egdmitry