packages feed

prairie-0.0.3.0: src/Prairie/Traverse.hs

-- | This module shows how to 'traverse' over a 'Record', allowing you to
-- perform side-effects for each field and return a transformed value.
--
-- @since 0.0.3.0
module Prairie.Traverse where

import Data.List (foldl')
import Prairie.Class
import Control.Applicative (liftA2)
import Prairie.Fold

-- | Apply an effectful function over each field of a 'Record', producing
-- a new 'Record' in the process.
--
-- This example use increments a @User@s age and requests a new name.
--
-- @
-- happyBirthday :: User -> IO User
-- happyBirthday =
--     traverseRecord
--          (\\val field ->
--              case field of
--                  UserName -> do
--                      putStrLn $ "Current name is: " <> val
--                      putStrLn "Please input a new name: "
--                      getLine
--                  UserAge -> do
--                      putStrLn $ "Current age is " <> show val
--                      pure (val + 1)
--          )
-- @
--
-- If you only want to target a single field, you can use a wildcard match
-- and `pure`. This example also uses @LambdaCase@.
--
-- @
-- nameAtLeastOneCharacter :: User -> 'Maybe' User
-- nameAtLeastOneCharacter =
--      'traverseRecord'
--          (\val ->
--              \case
--                  UserName -> do
--                      'guard' ('length' val >= 1)
--                      'pure' val
--                  _ ->
--                      'pure' val
--          )
-- @
--
-- @since 0.0.3.0
traverseRecord
    :: forall rec f. (Record rec, Applicative f)
    => (forall ty. ty -> Field rec ty -> f ty)
    -> rec
    -> f rec
traverseRecord f init =
    foldl' k (pure init) (recordToFieldList init)
  where
    f' :: Field rec ty -> ty -> f (SomeFieldWithValue rec)
    f' field val =
        SomeFieldWithValue field <$> f val field
    k frec (SomeFieldWithValue field val) =
        liftA2
            (\(SomeFieldWithValue field' val') rec -> setRecordField field' val' rec)
            (f' field val)
            frec