Voy a tratar de explicar el razonamiento para runST
's Tipo:
runST :: (forall s. ST s a) -> a
Y por qué no es así simple:
alternativeRunST :: ST s a -> a
Tenga en cuenta que este alternativeRunST
le habían trabajado para tu programa
alternativeRunST
También habría habían permitido a filtrarse las variables fuera de la mónada 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)
entonces usted podría ir en ghci y hacer:
>>> map evilFunction [7,7,7]
[7,8,9]
evilFunction
no es referencialmente transparente!
Por cierto, a lo pruebe aquí es el marco "malo ST" necesario para ejecutar el código anterior:
{-# 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)
El verdadero runST
no nos permiten construir tales funciones "mal". ¿Cómo lo hace? Es un poco complicado, ver más abajo:
Intentar ejecutar:
>>> 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"
no encaja (forall s. ST s a)
. Como puede ser visto también usando un ejemplo aún más simple, donde GHC nos da una bonita error:
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
Tenga en cuenta que también podemos escribir
dontEvenRunST :: forall a. (forall s. ST s a) -> Int
Y es equivalente a la omisión de la forall a.
como hemos hecho antes
Tenga en cuenta que el alcance de a
es mayor que el de s
, pero en el caso de newSTRef "Hi"
su valor debe depender de s
. El sistema de tipo no permite esto.
Guau, eso es maravilloso! Gracias por la explicación detallada. –