Beautiful functional programming

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.

2 Likes