I feel like this is a good candidate for runST
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
import Control.Monad (when)
import Control.Monad.ST
import Data.Aeson
import Data.STRef
import Data.Traversable (for)
newtype SectionList = SectionList [Section]
data Section = Section
{ title :: String
, reset_lesson_position :: Bool
, lessons :: [Lesson]
}
instance ToJSON SectionList where
toJSON (SectionList xs) = toJSON $ runST $ do
lessonPos <- newSTRef 1
for (zip [1 :: Int ..] xs) $ \(sectionPos, Section{..}) -> do
when reset_lesson_position $ writeSTRef lessonPos 1
lessons <- for lessons $ \Lesson{..} -> do
pos <- readSTRef lessonPos
modifySTRef lessonPos (+ 1)
pure $ object ["name" .= name, "position" .= Number (fromIntegral pos)]
pure $
object
[ "title" .= title
, "reset_lesson_position" .= reset_lesson_position
, "lessons" .= toJSON lessons
, "position" .= Number (fromIntegral sectionPos)
]
newtype Lesson = Lesson {name :: String}
sections :: SectionList
sections =
SectionList
[ Section
{ title = "Getting started"
, reset_lesson_position = False
, lessons =
[ Lesson{name = "Welcome"}
, Lesson{name = "Installation"}
]
}
, Section
{ title = "Basic operator"
, reset_lesson_position = False
, lessons =
[ Lesson{name = "Addition / Subtraction"}
, Lesson{name = "Multiplication / Division"}
]
}
, Section
{ title = "Advanced topics"
, reset_lesson_position = True
, lessons =
[ Lesson{name = "Mutability"}
, Lesson{name = "Immutability"}
]
}
]
main :: IO ()
main = print $ encode sections
-- $> main
EDIT: While structurally not as “pretty” as the python code, I feel like it gets close in spirit.