我會盡力解釋爲runST
的類型推理:
runST :: (forall s. ST s a) -> a
,爲什麼它是不是這樣簡單的一個:
alternativeRunST :: ST s a -> a
注意,這alternativeRunST
將工作過爲你的程序。
alternativeRunST
也已經讓我們泄露變量出ST
單子:
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-26 13:57:05