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

>   return x = Prob [(x,1 % 1)]

>   m >>= f = flatten (fmap f m)

>   fail _ = Prob []

Поскольку мы уже сделали всю тяжелую работу, экземпляр очень прост. Мы определили функцию >fail, которая такова же, как и для списков, поэтому если при сопоставлении с образцом в выражении >do происходит неудача, неудача случается в контексте списка вероятностей.

Важно также проверить, что для только что созданной нами монады выполняются законы монад:

1. Первое правило говорит, что выражение >return x >>= f должно равняться выражению >f x. Точное доказательство было бы довольно громоздким, но нам видно, что если мы поместим значение в контекст по умолчанию с помощью функции >return, затем отобразим это с помощью функции, используя >fmap, а потом отобразим результирующий список вероятностей, то каждая вероятность, являющаяся результатом функции, была бы умножена на вероятность >1 % 1, которую мы создали с помощью функции >return, так что это не повлияло бы на контекст.

2. Второе правило утверждает, что выражение >m >> return ничем не отличается от >m. Для нашего примера доказательство того, что выражение >m >> return равно просто >m, аналогично доказательству первого закона.

3. Третий закон утверждает, что выражение >f <=< (g <=< h) должно быть аналогично выражению >(f <=< g) <=< h. Это тоже верно, потому что данное правило выполняется для списковой монады, которая составляет основу для монады вероятностей, и потому что умножение ассоциативно. >1 % 2 * (1 % 3 * 1 % 5) равно >(1 % 2 * 1 % 3) * 1 % 5.

Теперь, когда у нас есть монада, что мы можем с ней делать? Она может помочь нам выполнять вычисления с вероятностями. Мы можем обрабатывать вероятностные события как значения с контекстами, а монада вероятностей обеспечит отражение этих вероятностей в вероятностях окончательного результата.

Скажем, у нас есть две обычные монеты и одна монета, с одной стороны налитая свинцом: она поразительным образом выпадает на решку девять раз из десяти и на орла – лишь один раз из десяти. Если мы подбросим все монеты одновременно, какова вероятность того, что все они выпадут на решку? Во-первых, давайте создадим значения вероятностей для подбрасывания обычной монеты и для монеты, налитой свинцом:

>data Coin = Heads | Tails deriving (Show, Eq)


>coin :: Prob Coin

>coin = Prob [(Heads,1 % 2),(Tails,1 % 2)]


>loadedCoin :: Prob Coin

>loadedCoin = Prob [(Heads,1 % 10),(Tails,9 % 10)]

И наконец, действие по подбрасыванию монет:

>import Data.List (all)


>flipThree :: Prob Bool

>flipThree = do

>   a <– coin

>   b <– coin

>   c <– loadedCoin

>   return (all (==Tails) [a,b,c])