haskell - quiere - neo monadología
¿Cómo funciona la mónada ST? (2)
El s es solo un truco que hace que el sistema de tipo te detenga haciendo cosas que no serían seguras. No "hace" nada en tiempo de ejecución; simplemente hace que el verificador de tipos rechace programas que hacen cosas dudosas. (Es el llamado tipo fantasma , una cosa que solo existe en la cabeza del verificador de tipos, y que no afecta nada en el tiempo de ejecución).
Entiendo que la mónada ST es algo así como un hermano pequeño de IO, que a su vez es la mónada estatal con magia RealWorld añadida. Puedo imaginar estados y puedo imaginar que RealWorld está de alguna manera puesto en IO, pero cada vez que escribo una firma ST de ST la s de la mónada ST me confunde.
Tomemos, por ejemplo, ST s (STArray sab) . ¿Cómo funciona el s allí? ¿Se usa solo para crear una dependencia de datos artificial entre cálculos sin que se pueda hacer referencia a ellos como estados en la mónada de estado (debido a la forall )?
Solo lanzo ideas y realmente agradecería que alguien más conocedor que yo me lo explique.
La s mantiene los objetos dentro de la mónada ST para que se filtren hacia el exterior de la mónada ST .
-- This is an error... but let''s pretend for a moment...
let a = runST $ newSTRef (15 :: Int)
b = runST $ writeSTRef a 20
c = runST $ readSTRef a
in b `seq` c
De acuerdo, este es un error de tipo (¡lo cual es bueno! ¡No queremos que STRef filtre fuera del cálculo original!). Es un error de tipo debido a los s adicionales. Recuerde que runST tiene la firma:
runST :: (forall s . ST s a) -> a
Esto significa que la s en el cálculo que está ejecutando no debe tener restricciones. Entonces cuando intentas evaluar a :
a = runST (newSTRef (15 :: Int) :: forall s. ST s (STRef s Int))
El resultado tendría el tipo STRef s Int , que es incorrecto ya que el s ha "escapado" fuera del campo en runST . Las variables de tipo siempre tienen que aparecer en el interior de un forall , y Haskell permite cuantificadores forall implícitos en todas partes. Simplemente no hay una regla que le permita descubrir de manera significativa el tipo de retorno de a .
Otro ejemplo con forall : para mostrar claramente por qué no se puede permitir que las cosas escapen de un forall , aquí hay un ejemplo más simple:
f :: (forall a. [a] -> b) -> Bool -> b
f g flag =
if flag
then g "abcd"
else g [1,2]
> :t f length
f length :: Bool -> Int
> :t f id
-- error --
Por supuesto, f id es un error, ya que devolvería una lista de Char o una lista de Int dependiendo de si el booleano es verdadero o falso. Simplemente está mal, al igual que el ejemplo con ST .
Por otro lado, si no tuvieras el parámetro tipo s , entonces todo tipo de verificación sería correcto, aunque el código obviamente es bastante falso.
Cómo funciona realmente el ST: En términos de implementación, la mónada ST es en realidad la misma que la mónima IO pero con una interfaz ligeramente diferente. Cuando usas la mónada ST , obtienes unsafePerformIO o su equivalente, detrás de las escenas. La razón por la que puede hacer esto de manera segura es debido a la firma de tipo de todas las funciones relacionadas con ST , especialmente la parte con el forall .