Случайное значение пользовательского типа данных в Haskell

Я определил следующий тип данных:

data NewBool = Truth | Lie deriving (Show)

и я создал функцию, которая должна возвращать случайное значение NewBool

giveMeBool :: IO()
giveMeBool = do 
     bool <- randomIO :: IO NewBool
     putStrLn("Your random boolean is"++ show bool)

Я читал здесь, что мне нужно сделать NewBool ​​экземпляром Random для использования randomIO. Итак, я сделал это:

 instance Random NewBool where
 random g = case random g of
              (r,g')  | r < (1/2)= (Truth, g')
                      | otherwise= (Lie, g')

Искренне это просто копия и вставка того, что я нашел в другом посте, но я не совсем понимаю, как это работает? Как определяется значение и тип r? Может кто-нибудь помочь мне? Спасибо ;)


person GniruT    schedule 20.03.2014    source источник
comment
Вы знаете, что существует экземпляр Random Bool?   -  person Zeta    schedule 20.03.2014


Ответы (1)


Поскольку random имеет тип (по существу)

random :: Random a => StdGen -> (a, StdGen)

если вы передадите ему StdGen, который у вас есть, g, он даст вам значение любого типа, создающего экземпляр типа Random. Компилятор угадывает, какой тип вам нужен, по тому, как вы его используете. В вашем случае он используется во фрагменте

r < (1/2)

и мы можем изучить типы каждой из этих частей

(<)   :: Ord a => a -> a -> Bool
(1/2) :: Fractional a => a

которые вместе означают, что полный тип r

r :: (Random a, Fractional a, Ord a) => a

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

default (Integer, Double)

это означает, что если у вас есть неоднозначный тип, ограниченный классами, и либо Integer, либо Double могут удовлетворять этим классам, то в этом порядке эти конкретные типы заменяются. В случае r, Double создает экземпляры Random, Ord и Fractional поэтому компилятор выбирает r :: Double.

Наконец, мы рассматриваем экземпляр Random для Double, который случайно выбирает Double значения в интервале (0,1). Это означает, что с вероятностью 50 % он будет больше, чем (1/2), и поэтому каждый из конструкторов Truth и Lie будет выбран с равными шансами.


Наконец, для реальной реализации Random NewBool, возможно, лучше загрузить экземпляр из очень похожего экземпляра, доступного с Bool.

instance Random NewBool where
  random g = case random g of
    (True,  g') -> (Truth, g')
    (False, g') -> (Lie,   g')

хотя это работает только в том случае, если вы не хотите изменять поведение генератора случайных чисел по сравнению с Random Bool. Однако обратите внимание, что здесь происходит аналогичное разрешение типов, за исключением того, что оно намного упрощено, поскольку я сопоставляю шаблоны True и False, что сразу означает, что возвращаемый тип random g должен быть (Bool, StdGen).

person J. Abrahamson    schedule 20.03.2014
comment
Я хотел бы упомянуть, что минимальное полное определение также требует randomR. Но это также может быть загружено. - person Zeta; 20.03.2014