Как я могу скрыто инициализировать состояние в Haskell (как это делает PRNG)?

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

  •  13-09-2019
  •  | 
  •  

Вопрос

Я просмотрел несколько уроков по монаде State и, думаю, у меня появилась идея.

Например, как в этот хороший урок:

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

Хорошо, я могу использовать getRandom:

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

Но мне все равно нужно передавать начальное значение PRNG каждый раз, когда я его вызываю.Я знаю, что PRNG, доступный в реализациях Haskell, не нуждается в этом:

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

Поэтому я, вероятно, неправильно понял государственную монаду, потому что то, что я мог видеть в большинстве учебных пособий, не кажется «настойчивым» состоянием, а просто удобным способом погрузить государство.

Так...Как я могу иметь состояние, которое автоматически инициализируется (возможно из некоторых функция, которая использует время и другие не очень предсказуемые данные), как модуль Random делает?

Большое спасибо!

Это было полезно?

Решение

randomRIO использует IO монада.Кажется, это хорошо работает в интерпретаторе, потому что интерпретатор также работает в IO монада.Это то, что вы видите в своем примере;на самом деле вы не можете сделать это на верхнем уровне кода - вам все равно придется поместить это в выражение do, как и все монады.

В общем коде вам следует избегать монады ввода-вывода, потому что, как только ваш код использует монаду ввода-вывода, он навсегда привязан к внешнему состоянию - вы не можете выйти из него (т.если у вас есть код, использующий монаду IO, любой код, который его вызывает, также должен использовать монаду IO;безопасного способа «выйти» из него не существует).Итак IO монаду следует использовать только для таких вещей, как доступ к внешней среде, где это абсолютно необходимо.

Для таких вещей, как локальное автономное состояние, не следует использовать монаду IO.Вы можете использовать State монада, как вы упомянули, или вы можете использовать ST монада.Монада ST содержит множество тех же функций, что и монада IO;то естьесть STRef мутабельные клетки, аналогичные IORef.И самое приятное в ST по сравнению с IO заключается в том, что когда вы закончите, вы можете позвонить runST на монаде ST, чтобы получить результат вычислений из монады, чего нельзя сделать с помощью IO.

Что касается «скрытия» состояния, это просто часть синтаксиса do-выражений в Haskell для монад.Если вы считаете, что вам нужно явно передать состояние, значит, вы неправильно используете синтаксис монады.

Вот код, который использует IORef в монаде 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

Вот код, использующий монаду ST:

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

Простота кода по сути такая же;за исключением того, что в последнем случае мы можем получить значение из монады, а в первом мы не можем, не поместив его в другое вычисление ввода-вывода.

Другие советы

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

Теперь получите доступ к вашему secretStateValue обычным способом. чтениеIORef и записьIORef, в монаде IO.

Поэтому я, вероятно, неправильно понял монаду State, потому что в большинстве руководств я видел не «постоянное» состояние, а просто удобный способ создания состояния потока.

Монада состояния — это именно прохождение состояния через некоторую область видимости.

Если вам нужно состояние верхнего уровня, оно находится за пределами языка (и вам придется использовать глобальную изменяемую переменную).Обратите внимание, как это, вероятно, усложнит потокобезопасность вашего кода — как инициализируется это состояние?и когда?И по какой ветке?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top