私は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
に依存すべき
注意。タイプシステムはこれを許可しません。
うわー、それは素晴らしいです!詳細な説明をありがとう。 –