I have data types with many fields that, if not being manually specified by a JSON configuration file, should be randomly set. I am using Aeson to parse the config file. What is the best way to do this?
Currently, I am setting values equal to some impossible value, and then later checking for said value to edit.
data Example = Example { a :: Int, b :: Int }
default = Example 1 2
instance FromJSON Example where
parseJSON = withObject "Example" $ \v -> Example
<$> (v .: "a" <|> return (a default))
<*> (v .: "b" <|> return (b default))
initExample :: Range -> Example -> IO Example
initExample range (Example x y) = do
a' <- if x == (a default) then randomIO range else return x
b' <- if y == (b default) then randomIO range else return y
return $ Example a' b'
What I would like is something along the lines of:
parseJSON = withObject "Example" $ \v -> Example
<$> (v .: "a" <|> return (randomRIO (1,10))
Is it possible to define Parsers in the IO Monad or thread along some random generator, ideally using Aeson?
Well, I don't know if it's a good idea, and juggling the extra IO
layer will certainly get frustrating as heck for larger developments, but the following type-checks:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Applicative
import Data.Aeson
import System.Random
data Example = Example { a :: Int, b :: Int } deriving (Eq, Ord, Read, Show)
instance FromJSON (IO Example) where
parseJSON = withObject "Example" $ \v -> liftA2 Example
<$> ((pure <$> (v .: "a")) <|> pure (randomRIO (3, 4)))
<*> ((pure <$> (v .: "b")) <|> pure (randomRIO (5, 6)))
In each of the last two lines, the first pure
is Int -> IO Int
, and the second is IO Int -> Parser (IO Int)
. In ghci:
> sequence (decode "{}") :: IO (Maybe Example)
Just (Example {a = 4, b = 6})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With