Изучай Haskell во имя добра! (Липовача) - страница 262

из модуля >System.Random имеет следующий тип:

>random :: (RandomGen g, Random a) => g –> (a, g)

Это значит, что она берёт генератор случайных чисел и производит случайное число вместе с новым генератором. Нам видно, что это вычисление с состоянием, поэтому мы можем обернуть его в конструктор >newtype State при помощи функции >state, а затем использовать его в качестве монадического значения, чтобы передача состояния обрабатывалась за нас:

>import System.Random

>import Control.Monad.State


>randomSt :: (RandomGen g, Random a) => State g a

>randomSt = state random

Поэтому теперь, если мы хотим подбросить три монеты (>True – это «решка», а >False – «орёл»), то просто делаем следующее:

>import System.Random

>import Control.Monad.State


>threeCoins :: State StdGen (Bool, Bool, Bool)

>threeCoins = do

>   a <– randomSt

>   b <– randomSt

>   c <– randomSt

>   return (a, b, c)

Функция >threeCoins – это теперь вычисление с состоянием, и после получения исходного генератора случайных чисел она передаёт этот генератор в первый вызов функции >randomSt, которая производит число и новый генератор, передаваемый следующей функции, и т. д. Мы используем выражение >return (a, b, c), чтобы представить значение >(a, b, c) как результат, не изменяя самый последний генератор. Давайте попробуем:

>ghci> runState threeCoins (mkStdGen 33)

>((True,False,True),680029187 2103410263)

Теперь выполнение всего, что требует сохранения некоторого состояния в промежутках между шагами, в самом деле стало доставлять значительно меньше хлопот!

Свет мой, Error, скажи, да всю правду доложи

К этому времени вы знаете, что монада >Maybe используется, чтобы добавить к значениям контекст возможной неудачи. Значением может быть >Just <нечто> либо >Nothing. Как бы это ни было полезно, всё, что нам известно, когда у нас есть значение >Nothing, – это состоявшийся факт некоей неудачи: туда не втиснуть больше информации, сообщающей нам, что именно произошло.

И тип >Either e a позволяет нам включать контекст возможной неудачи в наши значения. С его помощью тоже можно прикреплять значения к неудаче, чтобы они могли описать, что именно пошло не так, либо предоставить другую полезную информацию относительно ошибки. Значение типа >Either e a может быть либо значением >Right (правильный ответ и успех) либо значением >Left (неудача). Вот пример:

>ghci> :t Right 4

>Right 4 :: (Num t) => Either a t

>ghci> :t Left "ошибка нехватки сыра"

>Left "ошибка нехватки сыра" :: Either [Char] b

Это практически всего лишь улучшенный тип >Maybe, поэтому имеет смысл, чтобы он был монадой. Он может рассматриваться и как значение с добавленным контекстом возможной неудачи, только теперь при возникновении ошибки также имеется прикреплённое значение.