Como posso inicializar o estado de uma forma escondida em Haskell (como o PRNG faz)?

StackOverflow https://stackoverflow.com/questions/1121340

  •  13-09-2019
  •  | 
  •  

Pergunta

Eu passei por alguns tutoriais sobre a Mônada Estado e eu acho que eu tive a idéia.

Por exemplo, como em este bom tutorial :

import Data.Word

type LCGState = Word32

lcg :: LCGState -> (Integer, LCGState)
lcg s0 = (output, s1) 
  where s1 = 1103515245 * s0 + 12345
        output = fromIntegral s1 * 2^16 `div` 2^32


getRandom :: State LCGState Integer
getRandom = get >>= \s0 -> let (x,s1) = lcg s0
                           in put s1 >> return x

OK, para que eu possa usar getRandom:

*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 0
(0,12345)
*Main> runState getRandom 1              
(16838,1103527590)

Mas eu ainda preciso passar a semente ao PRNG cada vez que eu chamá-lo. Eu sei que o O PRNG disponível em implementações de Haskell não precisa que:

Prelude> :module Random
Prelude Random> randomRIO (1,6 :: Int)
(...) -- GHC prints some stuff here
6
Prelude Random> randomRIO (1,6 :: Int)
1

Então, eu provavelmente mal o Estado Mônada porque o que eu podia ver na maioria dos tutoriais não parece ser o estado "persistente", mas apenas uma maneira conveniente de estado do segmento.

Então ... Como posso ter estado que é automaticamente inicializado (possível a partir de alguns função que usa o tempo e outros dados não muito previsível), como o módulo Aleatório faz?

Muito obrigado!

Foi útil?

Solução

randomRIO usa a Mônada IO. Isso parece funcionar muito bem no interpretador porque o intérprete também trabalha na mônada IO. Isso é o que você está vendo no seu exemplo; você não pode realmente fazer isso no nível superior em código -. você teria que colocá-lo em uma expressão fazer como todos os mônadas de qualquer maneira

No código geral, você deve evitar a IO Mônada, porque uma vez que seu código usa a IO Mônada não está ligado a estado externo para sempre - você não pode sair dela (ou seja, se você tem o código que usa a mônada IO , qualquer código que chama ele também tem que usar o IO mônada, não há maneira segura de "sair" do mesmo). Assim, a Mônada IO só deve ser usado para coisas como acesso ao ambiente externo, as coisas onde é absolutamente necessário.

Para coisas como estado independente local, você não deve usar a mônada IO. Você pode usar a Mônada State como você mencionou, ou você pode usar a Mônada ST. A Mônada ST contém um monte das mesmas características que a mônada IO; isto é, há células mutáveis ??STRef, análogos aos IORef. E a coisa agradável sobre ST comparação com IO é que quando você é feito, você pode chamar runST em uma ST mônada para obter o resultado do cálculo fora da Mônada, que você não pode fazer com IO.

Como para "esconder" o Estado, que só vem como parte da sintaxe do-expressões em Haskell para mônadas. Se você acha que precisa para passar explicitamente o estado, então você não está usando a sintaxe mônada corretamente.

Aqui está o código que usa IORef no IO Mônada:

import Data.IORef
foo :: IO Int -- this is stuck in the IO monad forever
foo = do x <- newIORef 1
         modifyIORef x (+ 2)
         readIORef x
-- foo is an IO computation that returns 3

Aqui está o código que utiliza o ST mônada:

import Control.Monad.ST
import Data.STRef
bar :: Int
bar = runST (do x <- newSTRef 1
                modifySTRef x (+ 2)
                readSTRef x)
-- bar == 3

A simplicidade do código é essencialmente o mesmo; Só que neste último caso, podemos obter o valor fora da Mônada, e na antiga não podemos sem colocá-lo dentro de outro computação IO.

Outras dicas

secretStateValue :: IORef SomeType
secretStateValue = unsafePerformIO $ newIORef initialState
{-# NOINLINE secretStateValue #-}

Agora acessar sua secretStateValue com normalidade readIORef e writeIORef , no IO mônada.

Então, eu provavelmente mal o Estado Mônada porque o que eu podia ver na maioria dos tutoriais não parece ser o estado "persistente", mas apenas uma maneira conveniente de estado do segmento.

A Mônada estado é precisamente sobre enfiar Estado por meio de algum espaço.

Se você quiser estado de nível superior, que está fora da linguagem (e você terá que usar uma variável mutável global). Observe como isso provavelmente vai complicada segurança do segmento de seu código - como é que o estado inicializado? e quando? E por qual thread?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top