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