> module Data.Distribution (Distribution(..), Probability, probability, (<?), (?>), choose, (<~~), chooseMany) where
>
> import Data.Maybe (fromMaybe)
> import Data.Functor.Extras ((<$$>))
> import Data.Ratio ((%))
> import Control.Monad.Random (evalRand, fromList)
> import System.Random (RandomGen(..))
We represent probabilities as ratios of `Integer`s , with no strict
bounds-checking:
> type Probability = Rational
Distribution represents a discrete probability distribution. More
specifically: given some type parameter, a Distribution relates each
value of that type with the probability of its occurrence.
> type Distribution a = [(a, Probability)]
`choose` takes a random seed and a Distribution, and produces a value
according to that Distribution.
> choose :: RandomGen g => g -> Distribution a -> a
> choose seed = (`evalRand` seed) . fromList
>
> (<~~) :: RandomGen g => Distribution a -> g -> a
> (<~~) = flip choose
`chooseMany` takes a random seed and a Distribution, and produces an
infinite list of values according to that Distribution. Exercise: why
shouldn't this be implemented as `map choose . repeat . fromList`?
> chooseMany :: RandomGen g => g -> Distribution a -> [a]
> chooseMany seed = (`evalRand` seed) . sequence . repeat . fromList
`probability` takes a Distribution and a value, and returns the
likelihood of that value occurring per that distribution. Should a
probability not be defined for that value, this likelihood is zero:
> probability :: Eq a => a -> Distribution a -> Probability
> probability = fromMaybe (0%1) <$$> lookup
>
> (<?) :: Eq a => a -> Distribution a -> Probability
> (<?) = probability
>
> (?>) :: Eq a => Distribution a -> a -> Probability
> (?>) = flip probability