I am having difficulties wrapping my brain around RVarT in random-fu. Just as a mental exercise I am trying to generate Maybe x randomly and combining them in Maybe (x, x), using monad transformers
I have manged to pull this off, which seems intuitive to me
maybeRandSome :: (MaybeT RVar) Int
maybeRandSome = lift $ return 1
maybeRandNone :: (MaybeT RVar) Int
maybeRandNone = MaybeT . return $ Nothing
maybeTwoRands :: (MaybeT RVar) (Int, Int)
maybeTwoRands =
do
x <- maybeRandSome
y <- maybeRandNone
return (x, y)
And can sample them in IO doing this
> sample $ runMaybeT maybeTwoRands
Nothing
However I cannot figure out if the reverse is possible:
reverseMaybeRandSome :: (RVarT Maybe) Int
reverseMaybeRandSome = lift $ Just 1
reverseMaybeRandNone :: (RVarT Maybe) Int
reverseMaybeRandNone = lift Nothing
reverseMaybeTwoRands :: (RVarT Maybe) (Int, Int)
reverseMaybeTwoRands =
do
x <- Random.sample reverseMaybeRandSome
y <- Random.sample reverseMaybeRandNone
return (x, y)
Which requires me to lift from Maybe m to MonadRandom m somehow, and I can't figure out if that makes sense or if I am doing something unsound to begin with.
Yes, you're pretty much doing something unsound. MaybeT m a is isomorphic to m (Maybe a) for any monad, including m = RVar, so a MaybeT RVar a is really just an RVar (Maybe a), which is a representation of a random variable taking values in Maybe a. Given this, it's easy enough to imagine sampling two Maybe a-valued random variables and combining them into a Maybe (a,a)-value random variable in the usual manner (i.e., if either or both are Nothing, the result is Nothing, and if they're Just x and Just y respectively, the result is Just (x,y)). That's what your first chunk of code is doing.
However, RVarT Maybe a is different. It's a a-valued (not Maybe a-valued) random variable that can use the facilities of the base Maybe monad in generating its values, provided they can be lifted in some sensible way to the final monad in which the "randomness" of the random variable is realized.
To understand what this means, we have to take a more detailed look at the types RVar and RVarT.
The type RVar a represents an a-valued random variable. In order to actually turn this representation into a real random value, you have to run it with:
runRVar :: RandomSource m s => RVar a -> s -> m a
This is a little too general, so imagine it being specialized to:
runRVar :: RVar a -> StdRandom -> IO a
Note that StdRandom is the only valid value of StdRandom here, so we'll always write runRVar something StdRandom, which can also be written sample something.
With this specialization, you should view an RVar a as a monadic recipe for constructing a random variable using a limited set of randomization primitives that runRVar converts into IO actions that realize the randomization primitives with respect to a global random number generator. This conversion to IO actions is what allows the recipe to generate an actual sampled random value. If you're interested, you can find the limited set of randomization primitives in Data.Random.Internal.Source.
Similarly, RVarT n a is also an a-valued random variable (i.e., a recipe for constructing a random variable using a limited set of randomization primitives) that also has access to the "facilities of another base monad n". This recipe can be run inside any final monad that can realize both the randomization primitives and the facilities of the base monad n. In the general case, you run it with:
runRVarTWith :: MonadRandom m =>
(forall t. n t -> m t) -> RVarT n a -> s -> m a
which takes an explicit lifting function that explains how to lift the facilities of the base monad n to the final monad m.
If the base monad n is Maybe, then it's "facilities" are the ability to signal an error or failed computation. You might use those facilities to construct the following somewhat silly random variable:
sqrtNormal :: RVarT Maybe Double
sqrtNormal = do
z <- stdNormalT
if z < 0
then lift Nothing -- use Maybe facilities to signal error
else return $ sqrt z
Note that, critically, sqrtNormal does not represent a Maybe Double-valued random variable to be generated. Instead it represents Double-valued random variable whose generation can fail via the facilities of the base Maybe monad.
In order to realize this random variable (i.e., sample it), we need to run it in an appropriate final monad. The final monad needs to support both the randomization primitives and an appropriately lifted notion of failure from the Maybe monad.
IO works fine, if the appropriate notion of failure is a runtime error:
liftMaybeToIO :: Maybe a -> IO a
liftMaybeToIO Nothing = error "simulation failed!"
liftMaybeToIO (Just x) = return x
after which:
main1 :: IO ()
main1 = print =<< runRVarTWith liftMaybeToIO sqrtNormal StdRandom
generates the square root of a positive standard Gaussian about half the time and throws a runtime error the other half.
If you want to capture failure in a pure form (as a Maybe, for example), then you need to consider realizing the RVar in an appropriate monad. The monad:
MaybeT IO a
will do the trick. It's isomorphic to IO (Maybe a), so it has IO facilities available (needed to realize the randomization primitives) and is capable of signaling failure by returning Nothing. If we write:
main2 :: IO ()
main2 = print =<< runMaybeT act
where act :: MaybeT IO Double
act = sampleRVarTWith liftMaybe sqrtNormal
we'll get an error that there's no instance for MonadRandom (MaybeT IO). We can create one as follows:
import Control.Monad.Trans (liftIO)
instance MonadRandom (MaybeT IO) where
getRandomPrim = liftIO . getRandomPrim
together with an appropriate lifting function:
liftMaybe :: Maybe a -> MaybeT IO a
liftMaybe = MaybeT . return
After which, main2 will return Nothing about half the time and Just the square root of a positive Gaussian the other half.
The full code:
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Maybe (MaybeT(..))
import Data.Random
import Data.Random.Lift
import Data.Random.Internal.Source
sqrtNormal :: RVarT Maybe Double
sqrtNormal = do
z <- stdNormalT
if z < 0
then lift Nothing -- use Maybe facilities to signal error
else return $ sqrt z
liftMaybeToIO :: Maybe a -> IO a
liftMaybeToIO Nothing = error "simulation failed!"
liftMaybeToIO (Just x) = return x
main1 :: IO ()
main1 = print =<< runRVarTWith liftMaybeToIO sqrtNormal StdRandom
instance MonadRandom (MaybeT IO) where
getRandomPrim = liftIO . getRandomPrim
main2 :: IO ()
main2 = print =<< runMaybeT act
where act :: MaybeT IO Double
act = runRVarTWith liftMaybe sqrtNormal StdRandom
liftMaybe :: Maybe a -> MaybeT IO a
liftMaybe = MaybeT . return
The way this would all apply to your second example would look something like this, which will always print Nothing:
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Maybe (MaybeT(..))
import Data.Random
import Data.Random.Lift
import Data.Random.Internal.Source
reverseMaybeRandSome :: RVarT Maybe Int
reverseMaybeRandSome = return 1
reverseMaybeRandNone :: RVarT Maybe Int
reverseMaybeRandNone = lift Nothing
reverseMaybeTwoRands :: RVarT Maybe (Int, Int)
reverseMaybeTwoRands =
do
x <- reverseMaybeRandSome
y <- reverseMaybeRandNone
return (x, y)
instance MonadRandom (MaybeT IO) where
getRandomPrim = liftIO . getRandomPrim
runRVarTMaybe :: RVarT Maybe a -> IO (Maybe a)
runRVarTMaybe act = runMaybeT $ runRVarTWith liftMaybe act StdRandom
where
liftMaybe :: Maybe a -> MaybeT IO a
liftMaybe = MaybeT . return
main :: IO ()
main = print =<< runRVarTMaybe reverseMaybeTwoRands
Related
I'm trying to understand monad transformers. I have a code like this (that doesn't work):
import System.IO (hFlush, stdout)
import Text.Read (readMaybe)
add1 :: Int -> IO (Maybe Int)
add1 x = return $ Just (x + 1)
readNumber :: IO (Maybe Int)
readNumber = do
putStr "Say a number: "
hFlush stdout
inp <- getLine
return $ (readMaybe inp :: Maybe Int)
main :: IO ()
main = do
x <- readNumber >>= add1
print x
It throws
Couldn't match type ‘Int’ with ‘Maybe Int’
Expected: Maybe Int -> IO (Maybe Int)
Actual: Int -> IO (Maybe Int)
I figured out that I can make it work by introducing
(>>>=) :: IO (Maybe a) -> (a -> IO (Maybe b)) -> IO (Maybe b)
x >>>= f =
x >>= go f
where
go _ Nothing = return Nothing
go f (Just x) = f x
and using it instead of >>=. This is strikingly similar to a monad transformer, but I can't get my head around how exactly I should refactor this code to use it.
You may wonder "why does add1 return IO?" Let's say that it can be something more complicated that uses IO.
I'm looking to understand it better, so answers like "there is a better solution" or "it is already implemented in..." won't help. I would like to learn what I would need to change to make it work with >>= assuming that I want to do operations like IO (Maybe a) -> (a -> IO (Maybe b)) -> IO (Maybe b) that already work with my >>>=.
I'd say the most common way to use monad transformers is the mtl approach. That consists of using type classes like MonadIO and MonadFail to implement your programs and then in your main function use concrete transformers like MaybeT to instantiate the type classes and get the actual result.
For your program that can look like this:
import System.IO (hFlush, stdout)
import Text.Read (readMaybe)
import Control.Monad.Trans.Maybe (runMaybeT)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad (MonadFail (fail))
add1 :: Monad m => Int -> m Int
add1 x = pure (x + 1)
prompt :: String -> IO String
prompt x = do
putStr x
hFlush stdout
getLine
readNumber :: (MonadIO m, MonadFail m) => m Int
readNumber = do
inp <- liftIO (prompt "Say a number: ")
case readMaybe inp of
Nothing -> fail "Not a number"
Just x -> pure x
main :: IO ()
main = do
x <- runMaybeT (readNumber >>= add1)
print x
I need an list of biased, random booleans. Each boolean needs to have the same probability of being True (Bernoulli distributed). These booleans are passed to a function, which generates zero or more output booleans per input boolean. I need an infinite list, because I don't know in advance how many booleans are required to provide enough output. See the below (simplified) code:
import System.Random.MWC
import System.Random.MWC.Distributions
foo :: [Bool] -> [Bool] -- foo outputs zero or more Bools per input Bool
main = do
gen <- create
bits <- sequence . repeat $ bernoulli 0.25 gen
print . take 32 . foo $ bits
Unfortunately, this code just hangs at the second line of main. I guess that there is something non-lazy happening somewhere with Control.Monad.ST?
(I would be able to do something like this with System.Random.randoms, but the resulting values don't have the required distributions.)
Can I fix this while keep using the System.Random.MWC library? Or does this require me to switch to alternative implementations?
The mwc-random package provides two PrimMonad instances, one for IO and another for ST s. As long as an ST computation is parameterized over all state tags s, we can run the computation and extract the value with runST :: (forall s. ST s a) -> a. By itself this wouldn't be very useful since we'd lose the state: the seed of the random generator, but mwc-random also provides explicit ways to handle the seeds:
save :: PrimMonad m => Gen (PrimState m) -> m Seed
restore :: PrimMonad m => Seed -> m (Gen (PrimState m))
We can use these to make a computation that generates a stream of values from a computation that generates a single value, as long as the generator is in forall s. ST s.
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
import System.Random.MWC
import Control.Monad.ST
import System.Random.MWC.Distributions
randomStream :: forall s a. (forall s. GenST s -> ST s a) -> GenST s -> ST s [a]
randomStream item = go
where
go :: forall s. GenST s -> ST s [a]
go gen = do
x <- item gen
seed <- save gen
return (x:runST (restore seed >>= go))
With this we can write your example as
main = do
bits <- withSystemRandom (randomStream (bernoulli 0.25))
print . take 32 $ bits
We can actually build generators more sophisticated than using the same generator for each item in the stream. We could thread a state along the stream so that each value can depend on the results of the previous ones.
unfoldStream :: forall s a b. (forall s. b -> GenST s -> ST s (a, b)) -> b -> GenST s -> ST s [a]
unfoldStream item = go
where
go :: forall s. b -> GenST s -> ST s [a]
go b gen = do
(x,b') <- item b gen
seed <- save gen
return (x:runST (restore seed >>= go b'))
The following example stream has results that increase in likelihood every time the result is False.
import Control.Monad.Primitive
interesting :: (PrimMonad m) => Double -> Gen (PrimState m) -> m (Bool, Double)
interesting p gen = do
result <- bernoulli p gen
let p' = if result then p else p + (1-p)*0.25
return (result, p')
main = do
bits <- withSystemRandom (unfoldStream interesting 0)
print . take 32 $ bits
The culprit is sequence . repeat - this will hang for (almost?) every monad, since you must perform a potentially infinite number of effects.
The simplest solutions would be to use a different library - which may not be possible if you are relying on the quality of the numbers produced from mwc-random. The next simplest solution is to rewrite foo to have type [IO Bool] -> IO [Bool] and pass it repeat (bernoulli 0.25 gen) - this would allow foo to make the choice of when to stop executing the effects produced by the infinite list. But having your logic inside of IO is not very nice.
The standard trick when you need an infinite list of random numbers is to use a pure function f :: StdGen -> (Result, StdGen). Then unfoldr (Just . f) :: StdGen -> [Result], and the output is an infinite list. At first glance, it may appear that mwc-random only has monadic functions, and that there is no pure interface. However, that is not the case, because ST s is an instance of PrimMonad. You also have the functions converting a Gen to a Seed. Using these, you can get a pure RNG function for any monadic one:
{-# LANGUAGE RankNTypes #-}
import System.Random.MWC
import System.Random.MWC.Distributions
import Control.Monad.ST
import Data.List
pureRand :: (forall s . GenST s -> ST s t) -> Seed -> (t, Seed)
pureRand f s = runST $ do
s' <- restore s
r <- f s'
s'' <- save s'
return (r, s'')
pureBernoulli :: Double -> Seed -> (Bool, Seed)
pureBernoulli a = pureRand (bernoulli a)
foo :: [Bool] -> [Bool]
foo = id
main = do
gen <- create >>= save
let bits = unfoldr (Just . pureBernoulli 0.25) gen
print . take 32 . foo $ bits
It is unfortunate that mwc-random doesn't expose this sort of interface by default but it is pretty easy to get to.
The other option is slightly more scary - use unsafe functions.
import System.IO.Unsafe
repeatM rand = go where
go = do
x <- rand
xs <- unsafeInterleaveIO go
return (x : xs)
main2 = do
gen <- create
bits <- repeatM (bernoulli 0.25 gen)
print . take 32 . foo $ bits
Naturally this comes with the usual caveats surrounding unsafe - use it only if you are exceedingly inconvenienced by the pure functions. unsafeInterleaveIO may reorder or never execute effects - if foo, for example, ignores one element, it will never be computed and the corresponding effect of updating the state stored in gen may not happen. For example, the following will print nothing:
snd <$> ((,) <$> unsafeInterleaveIO (putStrLn "Hello") <*> return ())
Working in an IO computation I ended up with a staircase of case mbValue of …s and figured out that I should use the Maybe monad to simplify the code. Since it's within an IO computation and I need to get IO values, I used the MaybeT monad transformer so that I can lift IO computation into Maybe.
Now, I have always thought about values being “stripped” of their Maybeness after an values <- mbValue inside a Maybe computation, but this turns out to be too simple of a heuristic here.
As highlighted below, when using a Maybe a value as an a (here by passing it to read), it fails to type check:
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT)
lol :: IO (Maybe Int)
lol = return (Just 3)
lal :: IO (Maybe String)
lal = return (Just "8")
foo :: IO (Maybe Bool)
foo = do
b <- runMaybeT $ do
x <- lift lol
y <- lift lal
return (x < (read y))
return b ^-- Couldn't match type ‘Maybe String’ with ‘String’
main = foo >>= print
If I put a typed hole in for return (x < (read y)), I see that it expects a Bool, which makes sense, but also that the current bindings include
|| y :: Data.Maybe.Maybe GHC.Base.String
|| (bound at /private/tmp/test.hs:14:5)
|| x :: Data.Maybe.Maybe GHC.Types.Int
|| (bound at /private/tmp/test.hs:13:5)
I.e., y is a Maybe String. This of course explains the error, but I'm left confused. Where is my understanding wrong, and how can I fix this error?
In short: Replace lift by the MaybeT constructor.
Note that
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
and
lift :: (MonadTrans t, Monad m) => m a -> t m a
Your use of lift in
x <- lift lol
is at the type
lift :: IO (Maybe Int) -> MaybeT IO (Maybe Int)
That's why x will be a Maybe Int again. The lift adds a fresh MaybeT layer that is independent of the Maybe occurrence you already have.
But
MaybeT :: m (Maybe a) -> MaybeT m a
instead as in
x <- MaybeT lol
will be used at type
MaybeT :: IO (Maybe a) -> MaybeT IO a
and do the right thing.
When specialized to MaybeT, lift :: Monad m => m a -> MaybeT m a. Since lol :: IO (Maybe Int), m is IO and a is Maybe Int, therefore lift lol :: MaybeT IO (Maybe Int).
IO (Maybe a) is just the value contained within a MaybeT IO a newtype wrapper, so there's no need to lift it; instead use the MaybeT constructor, for example as in MaybeT lol.
But this is not how people tend to use monad transformers. Instead, just use MaybeT values and lift as needed:
import Control.Monad
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT, MaybeT)
lol :: MaybeT IO Int
lol = return 3
lal :: MaybeT IO String
lal = return "8"
foo :: IO (Maybe Bool)
foo =
runMaybeT $ do
x <- lol
y <- lal
_ <- lift getLine -- lift (IO String) to MaybeT IO String
_ <- return 100 -- lift any pure value
_ <- mzero -- use the MonadPlus instance to get a lifted Nothing.
return (x < (read y))
main = foo >>= print
Just learning how to get a deeper intuitive grasp of monads and transformers; a lot of things that might seem obvious are still kind of tricky to me haha.
So I have a computation that lives in the Rand monad, but inside it, there is another "sub-computation" (or multiple) that lives inside an ST monad (or State monad, for all it matters ... ST only for performance but I think State works just as well in this context).
The entire computation doesn't need to be inside the ST monad...and this sub-computation will be called multiple times with different starting states, so I don't want to coerce the entire thing into an ST (unless that's the idiomatic way).
Without randomness, the structure looks like this:
main = print mainComp
mainComp :: Int
mainComp = otherComp + (subComp 1) + (subComp 2)
subComp :: Int -> Int
subComp n = runST $ do
-- generate state based on n
-- ...
replicateM_ 100 mutateState
-- ...
-- eventually returns an ST s Int
mutateState :: ST s ()
mutateState = -- ...
Basically things work pretty well and there is complete referential transparency in mainComp and subComp.
This is how I've so far used Rand --
main = (evalRandIO mainComp) >>= print
mainComp :: (RandomGen g) => Rand g Int
mainComp = do
subResultA <- subComp 1
subResultB <- subComp 2
return $ otherComp + subResultA + subResultB
subComp :: (RandomGen g) => Int -> Rand g Int
subComp = return $ runST $ do -- is this ok to just throw in return?
-- generate state based on n
-- ...
replicateM_ 100 mutateState
-- ...
-- eventually returns an ST s Int (??)
mutateState :: ??
mutateState = ??
What is the type of mutateState supposed to be, if I wanted to use the random seed and the Rand monad in it? I think I might want to use a return type of RandT g (ST s) (), but how do I make that fit in with the type expected in the runST in subComp?
With monad transformers, you 'peel off' layers in the inverse order in which you add them. So, if you have something of type RandT g (ST s) (), you start by eliminating the RandT using evalRandT or runRandT, and only then calling runST.
Here's a simple example of combining RandT and ST:
import Data.STRef
import System.Random
import Control.Monad
import Control.Monad.Trans
import Control.Monad.ST
import Control.Monad.Random
import Control.Monad.Random.Class
stNrand :: RandT StdGen (ST s) Int
stNrand = do
ref <- lift $ newSTRef 0
i <- getRandomR (0,10)
lift $ writeSTRef ref i
lift $ readSTRef ref
main :: IO ()
main = putStrLn . show $ runST $ evalRandT stNrand (mkStdGen 77)
Edit: Here's an expanded version, now with a function runSTBelowRand that lets you embed RandT StdGen (ST s) a computations in Rand StdGen a computations. The function uses getSplit to split the seed of the global computation, and feed the new seed to the sub-computation.
{-# LANGUAGE RankNTypes #-}
import Data.STRef
import System.Random
import Control.Monad
import Control.Monad.Trans
import Control.Monad.ST
import Control.Monad.Random
import Control.Monad.Random.Class
stNrand :: RandT StdGen (ST s) Int
stNrand = do
ref <- lift $ newSTRef 0
i <- getRandomR (0,10)
lift $ writeSTRef ref i
lift $ readSTRef ref
runSTBelowRand :: (forall s. RandT StdGen (ST s) a) -> Rand StdGen a
runSTBelowRand r = do
splittedSeed <- getSplit
return $ runST $ evalRandT r splittedSeed
globalRand :: Rand StdGen (Int,Int)
globalRand = do
i1 <- runSTBelowRand stNrand
-- possibly non-ST stuff here
i2 <- runSTBelowRand stNrand
return (i1,i2)
main :: IO ()
main = putStrLn . show $ evalRand globalRand (mkStdGen 77)
Particularly, I need to be able to combine the CGI monad with the IO monad, but an example of how to combine the IO monad with the Maybe monad might be even better...
I assume you want to use the Maybe monad for early termination (like break or return in C).
In that case you should use MaybeT from the MaybeT package (cabal install MaybeT).
main = do
runMaybeT . forever $ do
liftIO $ putStrLn "I won't stop until you type pretty please"
line <- liftIO getLine
when ("pretty please" == line) mzero
return ()
MaybeT is a monad transformer version of the maybe monad.
Monad transformers "add functionality" to other monads.
You don't exactly say how you want to combine IO and Maybe, but I assume you have many functions that return IO (Maybe a) that you want to combine easily. Basically you want to treat IO (Maybe a) as a separate type with it's own Monad instance:
newtype IOMaybe a = IOM (IO (Maybe a))
-- "unpack" a value of the new type
runIOMaybe :: IOMaybe a -> IO (Maybe a)
runIOMaybe (IOM a) = a
instance Monad IOMaybe where
-- bind operator
(IOM ioa) >>= f = IOM $ do
a <- ioa
case a of
Nothing -> return Nothing
Just v -> runIOMaybe (f v)
-- return
return a = IOM (return (Just a))
-- maybe also some convenience functions
returnIO :: IO a -> IOMaybe a
returnIO ioa = IOM $ do
v <- ioa
return (Just v)
returnMaybe :: Maybe a -> IOMaybe a
returnMaybe ma = IOM (return ma)
With this you can use the do-Notation to combine functions that return IO (Maybe a), IO a or Maybe a:
f1 :: Int -> IO (Maybe Int)
f1 0 = return Nothing
f1 a = return (Just a)
main = runIOMaybe $ do
returnIO $ putStrLn "Hello"
a <- returnMaybe $ Just 2
IOM $ f1 a
return ()
Generally something that combines and modifies monads like this is called a monad transformer, and GHC comes with a package that includes monad transformers for common cases. If there is something in this monad transformer library that fits your scenario depends on how exactly you want to combine Maybe and IO.
In what sense do you want to combine the monads?
f :: Int -> IO (Maybe Int)
f x = do
putStrLn "Hello world!"
return $ if x == 0 then Nothing else Just x
Can be evaluated to:
[1 of 1] Compiling Main ( maybe-io.hs, interpreted )
Ok, modules loaded: Main.
*Main> f 0
Hello world!
Nothing
*Main> f 3
Hello world!
Just 3