Как использовать запись в системе.Случайный.Состояние

#haskell #random

Вопрос:

Я хочу использовать обозначения для объединения псевдослучайных значений:

 g :: StdGen
g = mkStdGen 100

example1 :: Bool
example1
  = fst
  $ runState
    (do x <- state (uniformR (False,True))
        y <- state (uniformR (False,True))
        return $ x == y
    )
    g
 

Функция uniformR определяется в терминах Системы.Случайный.Модуль с отслеживанием состояния:

 uniformR :: (RandomGen g, UniformRange a) => (a, a) -> g -> (a, g)
uniformR r g = runStateGen g (uniformRM r)
 

поэтому в моем примере кажется глупым uniformR создавать и запускать состояние, только в моем примере снова создавать и запускать состояние.

Есть ли способ переписать пример 1, используя систему.Случайный.Состояние и обозначения?

Это единственное, что я мог заставить работать (что смешно):

 example3 :: Bool
example3
  = fst
  $ runStateGen
    g
    (do x <- uniformRM (False,True)
        y <- uniformRM (False,True)
        return $ do x' <- x
                    y' <- y
                    return $ x'==y')
 

Похоже, мне нужен какой-то трансформатор монад?

Комментарии:

1. Я рекомендую проверить этот пост в блоге: alexey.kuleshevi.ch/blog/2021/01/29/random-interface или эта презентация: youtube.com/watch?v=GGbPqSM1ADw

Ответ №1:

Это гораздо проще, чем можно было бы себе представить:

 example4 :: Bool
example4 = runStateGen_ (mkStdGen 100) $ gen -> do
  x <- uniformRM (False,True) gen
  y <- uniformRM (False,True) gen
  pure (x == y)
 

Конечно uniformRM (False, True) == unformM , но это, вероятно, не имеет значения, поскольку, я полагаю, это был просто пример для демонстрации вопроса.

Более подробную информацию можно найти в пикше, а также в этом блоге и в этой видеопрезентации

Ответ №2:

Второй аргумент в runStateGen StateGenM g -> State g a пользу того , что это ReaderT замаскировано:

 import Control.Monad.Reader (ReaderT(..), runReaderT)
import Control.Applicative (liftA2)
import qualified System.Random.Stateful as Random

uniformRM :: (Random.UniformRange a, Random.StatefulGen g m) => (a, a) -> ReaderT g m a
uniformRM r = ReaderT (Random.uniformRM r)

g :: Random.StdGen
g = Random.mkStdGen 100

example1
  = Random.runStateGen_ g
  $ runReaderT
  $ liftA2 (==) (uniformRM (False,True)) (uniformRM (False,True))
 

Комментарии:

1. Это хороший подход, когда человек уже полагается ReaderT на свой поток управления и может быть смешан с HasGen env g линзами и Has рисунком. Например, это отлично сработало бы с rio пакетом. Для простых случаев использования я думаю, что это перебор, и ручная передача изменяемого генератора будет более естественной.