I have a simple little Happstack application that shows a form with an email field and a random question field to help combat spam. To get a random number I use getStdGen
in my main
function and pass it along to my function which creates the html. The problem is that the same StdGen
is used so my random value is not random unless I restart the application.
Here's what my Main.hs
looks like:
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
module Main where
import Happstack.Lite
import qualified Pages.Contact as Contact
import System.Random
main :: IO ()
main = do
gen <- getStdGen
serve Nothing $ pages gen
pages :: StdGen -> ServerPart Response
pages g = msum
[ dir "contact" $ Contact.page g
... Other irrelevant pages
]
And here's the function that uses the StdGen
to retrive a random question id:
getRandomQID :: StdGen -> Int
getRandomQID g =
let (rpercent, _) = random g :: (Float, StdGen)
rid = rpercent * questionsAmount
in round rid
questionsAmount :: (Num a) => a
questionsAmount = (fromIntegral . length) questions
What is the most elegant way to solve this problem?
As I wrote this question I found a solution that worked in the Happstack crash course (templates).
In your route which has a return type of ServerPart Response
you can use the liftIO
monad transformer to be able to perform IO actions. There's this handy function called randomRIO
with generates a random Int
from an input of a tuple which two Int
s as range, like this:
page :: ServerPart Response
page = do
randID <- liftIO $ randomRIO (0, max)
... Code to generate response ...
where max = length questions
randomRIO
can be found in System.Random
and liftIO
can be found in Control.Monad.Trans
.