arrays - Documentación de Starray para novatos y preguntas relacionadas con State/ST
haskell higher-rank-types (1)
Tengo dificultades para entender STArray
partir de la documentación y otros howtos / debates que he encontrado a través de Google. Tengo algunas preguntas más relacionadas a continuación.
De acuerdo con la documentación, STArray
s son
Matrices mutables en caja y sin caja en la mónada ST.
Esto me dio la impresión de que STArray
está destinado a ser utilizado como un estado que se transmite entre funciones (imagine que tiene un vector que debe actualizarse a menudo).
Aparentemente, esto se usa de manera diferente:
ST s (STArray s a e)
¿Cuál es el estado aquí? Si se usa internamente, ¿por qué no está oculto para el usuario?
Esto también significa que, si queremos utilizar un STArray s Int Int
se transmite como estado, uno definiría
type StateArray a = Control.Monad.State (ST s (STArray s Int Int)) a
que parece bastante engorroso.
Finalmente,
- ¿Cuál es la diferencia entre
ST
yState
? - ¿
STArray
es la diferencia entreSTArray
eIOArray
, si elST
y elIO
están diseñados para un uso "interno"?
¡¡Gracias!!
ST
es una mónada en la que se permiten un tipo limitado de efectos secundarios, a saber, las referencias mutables y las matrices mutables. Por lo tanto, le permite implementar funciones que son puras como se ven desde el mundo exterior, pero que usan la mutación internamente.
Esto es diferente del State
, que solo falsifica la mutación al enhebrar el estado a través de su computación como entradas y salidas adicionales. La diferencia es importante cuando se implementan algunos algoritmos imperativos, porque a veces es necesario implementar la mutación de manera eficiente. Por ejemplo, usando una matriz regular en una mónada de State
, solo puede modificarla haciendo una copia, mientras que con ST
puede tener una mutación verdadera en el lugar.
La razón por la que tenemos tanto ST
como IO
es que ST
proporciona garantías más sólidas que IO
, a saber:
-
ST
no permite efectos secundarios arbitrarios como, por ejemplo, acceder al sistema de archivos. - Podemos garantizar que los efectos secundarios que
ST
permite no pueden escapar del alcance derunST
, por lo que puede verse como puro del mundo exterior.
La razón por la que podemos garantizar que los efectos secundarios no puedan escapar está relacionada con la variable de tipo s
. Como cualquier acción ST debe ser polimórfica en s
, no puede escribir código que permita que cualquier referencia mutable ingrese o salga del alcance de un runST
, porque el verificador de tipos se quejará de que no puede garantizar que la s
de su acción y la de la referencia o array son lo mismo a menos que provengan del mismo alcance de runST
.
Como ejemplo del uso de la mónada ST
con matrices mutables, aquí hay una implementación de Sieve of Erathostenes:
import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed
primesUpto :: Int -> [Int]
primesUpto n = [p | (p, True) <- assocs $ sieve n]
sieve :: Int -> UArray Int Bool
sieve n = runSTUArray $ do
sieve <- newArray (2, n) True
forM_ [2..n] $ /p -> do
isPrime <- readArray sieve p
when isPrime $ do
forM_ [p*2, p*3 .. n] $ /k -> do
writeArray sieve k False
return sieve
runSTUArray
es una forma especializada de runST
que te permite construir una matriz usando una mutación en el interior, antes de congelarla y devolverla como una matriz inmutable. newArray
, readArray
y writeArray
hacen lo que cabría esperar.
Como puede ver, la firma tipo de sieve
indica que es una función pura, y lo es. Sin embargo, utiliza una gran mutación en el interior para implementarlo de manera eficiente.