Comment puis-je initialiser l'état d'une manière cachée dans Haskell (comme le fait PRNG)?

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

  •  13-09-2019
  •  | 
  •  

Question

Je suis passé par quelques tutoriels sur la monade de l'Etat et je pense que j'ai eu l'idée.

Par exemple, comme dans ce beau tutoriel :

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, je peux utiliser getRandom:

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

Mais je dois encore passer la semence au PRNG chaque fois que je l'appelle. Je sais que le PRNG disponible dans les mises en œuvre Haskell n'a pas besoin que:

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

Je probablement mal compris la monade de l'Etat, parce que ce que je pouvais voir dans la plupart des tutoriels ne semble pas être l'état « persistant », mais juste un moyen pratique à l'autre fil.

Alors ... Comment puis-je avoir l'état qui est automatiquement initialisé (possible à partir de certains fonction qui utilise le temps et d'autres données non-très prévisibles), comme le module au hasard fait?

Merci beaucoup!

Était-ce utile?

La solution

randomRIO utilise la monade IO. Cela semble fonctionner très bien dans l'interpréteur parce que l'interprète travaille également dans la monade IO. C'est ce que vous voyez dans votre exemple; vous ne pouvez pas vraiment faire au plus haut niveau dans le code -. vous devez le mettre dans un do-expression comme tous les monades de toute façon

Dans le code général, vous devez éviter la monade IO, car une fois votre code utilise la monade IO, il est lié à l'état externe pour toujours - vous ne pouvez pas sortir (si vous avez du code qui utilise la monade IO , tout code qui appelle doit aussi utiliser la monade IO, il n'y a aucun moyen sûr de « sortir » de celui-ci). Ainsi, la monade IO ne doit être utilisé pour des choses comme l'accès à l'environnement extérieur, les choses où il est absolument nécessaire.

Pour des choses comme état autonome local, vous ne devez pas utiliser la monade IO. Vous pouvez utiliser la monade State comme vous l'avez mentionné, ou vous pouvez utiliser la monade ST. La monade ST contient beaucoup des mêmes caractéristiques que la monade IO; à savoir il y a STRef cellules mutables, analogues aux IORef. Et la bonne chose à propos ST par rapport à IO est que lorsque vous avez terminé, vous pouvez appeler runST sur une monade ST pour obtenir le résultat du calcul de la monade, que vous ne pouvez pas faire avec IO.

Quant à « cacher » l'État, qui vient tout comme une partie de la syntaxe des do-expressions Haskell pour monades. Si vous pensez que vous devez passer explicitement l'état, vous n'utilisez pas la syntaxe monade correctement.

Voici le code qui utilise IORef dans la monade IO:

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

est le code ici qui utilise le ST monade:

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

La simplicité du code est essentiellement le même; sauf que dans ce dernier cas, nous pouvons obtenir la valeur de la monade, et dans l'ancien, nous ne pouvons pas sans le mettre dans un autre calcul IO.

Autres conseils

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

Accès maintenant votre secretStateValue avec readIORef et writeIORef , dans la monade IO.

  

Je probablement mal compris la monade de l'Etat, parce que ce que je pouvais voir dans la plupart des tutoriels ne semble pas être l'état « persistant », mais juste un moyen pratique à l'autre fil.

Le monade de l'Etat est précisément sur l'état de filetage à travers une certaine portée.

Si vous voulez état de haut niveau, qui est en dehors de la langue (et vous devrez utiliser une variable globale mutable). Notez comment cela sera probablement compliqué la sécurité fil de votre code - comment est-ce que l'état initialisé? et quand? Et par quel fil?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top