Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining RandT and MaybeT

I have a simple function using Control.Monad.Random to create a function which can sample a random number.

import Control.Monad.Random       
import Data.Random         

unif_bound :: (RandomGen g, Monad m) => Double -> Double -> RandT g m Double
unif_bound lb ub = getRandomR (lb,ub)

And, I run this to generate random numbers in GHCI as:

  > gen <- newStdGen
  > runRandT (unif_bound 1.0 3.0) gen
(1.7569726469904563,1700403094 44073136)  
  > runRandT (unif_bound 3.0, 1.0) gen
(1.7569726469904563,1700403094 44073136) 

However, I would like to modify this code to check that lb < ub, and wrap this in a MaybeT. The idea is that sampling where lb > ub should return Nothing. I realize that this is where monad transformers come in, but I have never used one before and not sure where to start.

For reference, RandT is defined as

-- | A monad transformer which adds a random number generator to an
-- existing monad.
newtype RandT g m a = RandT (StateT g m a)
    deriving (Functor, Monad, MonadTrans, MonadIO, MonadFix, MonadReader r, MonadWriter w)

Thanks!

like image 473
stevejb Avatar asked Nov 09 '22 22:11

stevejb


1 Answers

You can generalize the type of your function quite a bit:

unif_bound :: (Random a, Ord a, MonadRandom m) => a -> a -> MaybeT m a
unif_bound lb ub | lb > ub = MaybeT (return Nothing)
                 | otherwise = getRandomR (lb,ub)

Note that you could replace MaybeT (return Nothing) with fail "" because that is how fail is defined in the monad instance for MaybeT m, but this isn't very nice. Usage:

>runMaybeT $ unif_bound 1 10
Just 2
>runMaybeT $ unif_bound 400 1
Nothing

IO is an instance of MonadRandom, so you don't need to fiddle with RandT if you are just testing in the interpreter.

like image 84
user2407038 Avatar answered Dec 19 '22 19:12

user2407038