Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Parsers from Aeson with IO

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?

like image 757
Eric Klinkhammer Avatar asked Jan 30 '23 09:01

Eric Klinkhammer


1 Answers

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})
like image 177
Daniel Wagner Avatar answered Feb 07 '23 10:02

Daniel Wagner