2011-08-10 19 views
8

Estoy escribiendo un código (una muestra MCMC de Metropolis-Hastings) que utilizará un generador de números aleatorios y modificará una matriz y potencialmente otras estructuras basadas en esto.¿Una buena manera de realizar un seguimiento de varias referencias entre funciones en ST monad?

Mi idea inicial era usar la mónada ST, para poder usar las matrices ST y el paquete mersenne-random-pure64, manteniendo el generador PureMT como parte del estado.

Sin embargo, quiero poder separar parte del trabajo en funciones auxiliares separadas (por ejemplo, muestrear un entero aleatorio en un rango determinado, actualizar la estructura de la matriz y cosas potencialmente más complicadas). Para hacer esto, creo que necesitaría pasar las referencias al generador de PureMT y la matriz a todas las funciones, lo que rápidamente podría volverse muy feo si necesito almacenar más estado.

Mi instinto es agrupar todo el estado en un único tipo de datos al que pueda acceder en cualquier lugar, como usaría la mónada de estado definiendo un nuevo tipo de datos, pero no sé si eso es posible con el ST mónada, o la forma correcta de hacerlo.

¿Hay algún patrón agradable para hacer este tipo de cosas? Quiero mantener las cosas lo más generales posible, porque probablemente necesite agregar un estado extra y construir más código monádico alrededor de las partes existentes.

He intentado buscar ejemplos de código de mónada de ST, pero no parece estar cubierto en Real World Haskell, y los ejemplos de wiki de haskell son muy breves y simples.

gracias!

Respuesta

10

Mi instinto es agrupar todo el estado en un solo tipo de datos que pueda acceder a cualquier parte, como me gustaría utilizar la mónada Estado mediante la definición de un nuevo tipo de datos, pero no sé si eso es posible con la mónada ST, o la forma correcta de hacerlo.

¿Hay algún patrón agradable para hacer este tipo de cosas? Quiero mantener las cosas lo más generales posible, porque probablemente necesite agregar un estado extra y construir más código monádico alrededor de las partes existentes.

El punto clave aquí es darse cuenta de que es completamente irrelevante que está utilizando ST. Las referencias ST en sí mismas son solo valores regulares, a los que debe acceder en una variedad de lugares, pero no desea que realmente los cambie. La mutabilidad se produce en ST, pero los valores STRef y demás son básicamente de solo lectura. Son nombres que apuntan a los datos mutables.

Por supuesto, el acceso de solo lectura a un entorno ambiental es para lo que es la mónada Reader. El paso feo de las referencias a todas las funciones es exactamente lo que está haciendo por usted, pero como ya está en ST, puede simplemente atornillarlo como un transformador de mónada. Como un simple ejemplo, se puede hacer algo como esto:

newtype STEnv s e a = STEnv (ReaderT e (ST s) a) 
    deriving (Functor, Applicative, Monad) 

runEnv :: STEnv s e a -> ST s e -> ST s a 
runEnv (STEnv r) e = runReaderT r =<< e 

readSTEnv :: (e -> STRef s a) -> STEnv s e a 
readSTEnv f = STEnv $ lift . readSTRef . f =<< ask 

writeSTEnv :: (e -> STRef s a) -> a -> STEnv s e() 
writeSTEnv f x = STEnv $ lift . flip writeSTRef x . f =<< ask 

Para mayor generalidad, usted podría abstract over the details of the reference types, y convertirlo en un "ambiente con mutable referencias" en general mónada.

+0

Esto suena como una buena idea, olvidé que las referencias no están cambiando. ¡Gracias por esto! También me pregunto sobre el posible uso de la mónada de ST desde dentro de sí misma para separar cálculos estatales independientes que se ejecutan en un entorno de nivel superior al que no necesitan acceder, con su propio estado local oculto. ¿Tendría que usar el transformador de mónada ST para esto, o puede simplemente usar let a = runST $ .... dentro de la mónada ST? Si puede (sin usar el transformador), ¿qué pasaría si tratara de desreferenciar una referencia de la mónada adjunta (suponiendo que estarían dentro del alcance)? – Tom

+0

@Tom: no hay transformador para 'ST', como' IO', siempre está en la parte inferior de cualquier pila. Además, dos cómputos completos de 'ST', es decir, cosas que suceden dentro de una llamada a' runST', están completamente aislados del estado de cada uno, y cualquier intento de mezclarlos dará un error de tipo. Puede pasar valores opacos, tal como podría pasar 'IORef' dentro de' ST', pero tampoco puede * usar * ellos como tampoco podría usar 'IORef's. Sin embargo, puedes usar 'runST' como siempre; el resultado de eso es un valor puro, como siempre. –

+0

@Tom: Además, si utiliza un 'ReaderT' como en mi ejemplo, probablemente podría implementar varios tipos de operaciones de horquillado sin demasiados problemas: clonando el entorno actual para ejecutarlo en un' ST' interno separado, ejecutando algo en el mismo 'ST' con un entorno diferente, & c. –

5

Puede usar la mónada ST al igual que la mónada IO, teniendo en cuenta que solo obtiene matrices y referencias y ninguna otra ventaja IO. Al igual que IO, puede superponer un StateT sobre él si desea enhebrar algún estado de forma transparente a través de su cálculo.

+0

Oh, no pensé en eso, creo que eso solucionará mis problemas. – Tom

Cuestiones relacionadas