Я попытаюсь объяснить причины для типа 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
. Система типов этого не позволяет.
person
yairchu
schedule
26.07.2012