14

タイプのバインディングがあり、次のようにマップを使用して各要素[ST s (Int, [Int])]に適用しようとしています。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と関数の合成については知っていますが、これが当てはまるかどうかはわかりません。

みなさん、ありがとうございました!

4

3 に答える 3

15

で状態トランスを実行するたびにrunST、他のすべての状態トランスとは別のローカル状態で動作します。 新しいrunST状態タイプを作成し、そのタイプで引数を呼び出します。だから、例えば、あなたが実行する場合

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

その場合、1番目return ()と2番目return ()は異なるタイプになります:ST s1 ()およびST s2 ()、いくつかの未知のタイプについてはs1s2によって作成されrunSTます。

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

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

は多形であるためr1、その状態は、によって選択されたタイプを含め、任意のタイプを持つことができますrunSTrunST多態性のリストにマップすることができますr1(を使用して-XImpredicativeTypes):

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

runSTただし、非多型のリストにマップすることはできませんr1

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

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

すべてのリスト要素に同じ状態を与えても問題がない場合は、runST以下に示すように1回呼び出すことができます。明示的な型アノテーションは必要ありません。

runST (sequence ([r1, r1] :: forall t. [ST t ()]))
于 2012-07-26T05:51:43.520 に答える
6

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

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

'まったく同じ を持つ(Int、[Int])を返すステートフル計算のリスト'を意味しsます。しかし、次のタイプを見てくださいrunST

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

s このタイプは、「想像できるあらゆるものになり得るステートフル計算を行う関数」を意味します。これらのタイプの計算は同じものではありません。そして最後に:

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

ご覧のとおり、リストには現在よりも多くの多態的な値が含まれているはずです。sタイプはリストの各要素で異なる可能性があり、と同じタイプではない可能性がありますname。の型署名を変更するnameと、すべてOKになります。いくつかの拡張機能を有効にする必要があるかもしれませんが、GHCはどの拡張機能を教えてくれるはずです。

于 2012-07-26T05:51:57.810 に答える
5

runSTのタイプの理由を説明しようと思います。

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

そして、なぜそれがこの単純なもののようではないのですか?

alternativeRunST :: ST s a -> a

alternativeRunSTこれはあなたのプログラムでうまくいくだろうことに注意してください。

alternativeRunSTSTモナドから変数をリークすることもできたでしょう:

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に移動して次のことを実行できます。

>>> 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)。さらに単純な例を使用して見ることもできるように、GHCは私たちにかなり素晴らしいエラーを与えます:

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

私たちも書くことができることに注意してください

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

forall a.そして、それは前に行ったようにを省略することと同じです。

aのスコープはのスコープよりも大きいことに注意してください。ただし、その値sの場合newSTRef "Hi"はに依存する必要がありますs。型システムはこれを許可していません。

于 2012-07-26T23:01:54.073 に答える