A Reader Writer State Except monad stack. Useful when pure computations can fail—interpreters, compilers.
tell to log; messages are read at the end.Result but the API gives you throw, catch, and try which looks like imperative programming.Use RWSE over RWS when you need fail-fast error handling.
Declare small stacks at the top level, then compose them. Use throw and catch when a step can fail:
type Error
= ExceededMax Int
type alias Config =
{ step : Int, maxTotal : Int }
type alias State =
{ total : Int }
addStep : RWSE Config String State Error ()
addStep =
ask
|> andThen (\cfg -> modify (\s -> { s | total = s.total + cfg.step }))
|> andThen_ (tell [ "addStep" ])
double : RWSE Config String State Error ()
double =
modify (\s -> { s | total = s.total * 2 })
|> andThen_ (tell [ "double" ])
checkMax : RWSE Config String State Error ()
checkMax =
askGet
|> andThen
(\( cfg, s ) ->
if s.total > cfg.maxTotal then
throw (ExceededMax s.total)
else
tell [ "within limit " ++ String.fromInt s.total ]
)
computation : RWSE Config String State Error ()
computation =
addStep
|> andThen_ double
|> andThen_ checkMax
|> andThen_ addStep
-- runWith { step = 1, maxTotal = 5 } { total = 0 } computation
-- => ( { total = 3 }, [ "addStep", "double", "within limit", "addStep" ], Ok () )Just use unit () if you don't need a facet of this. For example,
type alias MyStack a = RWSE () () () () a that would basically just be Result.get, modify, and put the state but it would be unit so uninteresting.ask for the configuration but it would be unit so uninteresting.tell to log but the result would be a list of units which is uninteresting.Construct a stack from a raw function. Low-level; prefer pure, andThen, and the other combinators.
Execute the stack with config and state. Returns (finalState, logs, Result error a).
Same as run but takes config and state first. Pipeline-friendly: runWith config state myStack.
Create a stack from a Result. Ok becomes success; Err becomes a thrown error.
fromResult (Ok 42) -- succeeds with 42
fromResult (Err "fail") -- throws, halting the computationNo-op. Useful with andThen_ when you need a unit-producing computation.
Applicative apply. pure f |> andMap a |> andMap b lifts a two-argument function over the stack.
Monadic bind. Chain computations; each step receives the result of the previous.
Convenience function for where you have a computation that should happen after another where the preceding produces no value. It reads a lot nicer.
-- for example
pure ()
|> andThen_ (tell [ "Hello" ])
-- is nicer than
pure ()
|> andThen (\_ -> tell [ "Hello" ])Transform the result of a stack.
Map the result of a stack with knowledge of the config and state.
Shorthand for andMapping.
map2 Tuple.pair ask get -- RWSE config log state error ( config, state )Shorthand for andMapping.
map3 f a b c
-- is equivalent to
pure f |> andMap a |> andMap b |> andMap cShorthand for andMapping.
map4 f a b c d
-- is equivalent to
pure f |> andMap a |> andMap b |> andMap c |> andMap dShorthand for andMapping.
map5 f a b c d e
-- is equivalent to
pure f |> andMap a |> andMap b |> andMap c |> andMap d |> andMap eExecute a computation only when the condition is true; otherwise do nothing.
when (count > 0) (modify (\s -> { s | count = s.count - 1 }))Execute a computation only when the Maybe has a value.
whenJust (Just 42) (\n -> tell [ "got " ++ String.fromInt n ])Execute a computation only when the Result is Ok.
whenOk (Ok 42) (\n -> tell [ "success: " ++ String.fromInt n ])ALSO KNOWN AS sequence
Map each element through the stack and collect results. Also known as traverse. Fails fast on first error.
Like a for loop: run a computation for each element, discarding results. Fails fast on first error.
for_ [ 1, 2, 3 ] (\n -> tell [ "processing " ++ String.fromInt n ])Iterate over a list with an accumulator. Like List.foldl but the step runs in the stack (config, state, logging, fail-fast).
foldM (\acc x -> pure (acc + x)) 0 [ 1, 2, 3 ]Load the configuration as the value.
PLEASE see the following before using this
andModify if you want to modify the state based on the result of a prior computation, the configuration, and the current state.andTell if you want to log based on the result of a prior computation, the configuration, and the current state.mapWith if you want to map the value with knowledge of the configuration and the current state.Load one fact of the configuration as the value.
PLEASE see the following before using this
andModify if you want to modify the state based on the result of a prior computation, the configuration, and the current state.andTell if you want to log based on the result of a prior computation, the configuration, and the current state.mapWith if you want to map the value with knowledge of the configuration and the current state.Append to the log. All tell output is collected and returned at the end of run.
This convenience function allows you to log based on the result of a prior computation, the configuration, and the current state and then return the original value. So it acts like a kind of sink.
Append a single value to the log. Equivalent to tell [ x ].
Read the state into the value returned by the state. Only use this if you genuinely need to read the state for some reason.
andModify.andTell.Update the state by applying an update function.
See andModify for a more powerful version that includes the current state, the result of a prior computation, and the configuration.
This convenience function supports modifying the state based on the state, the result of a prior computation, and the configuration.
Recover from an error. If the stack failed, run the handler with the error; otherwise pass through the success.
throw (ParseError "bad")
|> catch (\err -> pure (defaultValue err))Throw an error and halt all computations. Stops chains of andThen, combine, combineMap, for_, and foldM.
throw (ParseError "unexpected token")
|> catch (\err -> pure 0) -- recovers with 0Turn a failed stack into a successful one by wrapping the result in Result. Errors become Err, successes become Ok.
try (throw (ParseError "oops")) -- Ok (Err (ParseError "oops"))
try (pure 42) -- Ok (Ok 42)Lift a computation that works on a child state to work on a parent state. Accepts record-shaped optics compatible with Monocle; Elm's structural typing means any record with get : parent -> child and set : child -> parent -> parent works.
lens = { get = .count, set = \v s -> { s | count = v } }
liftStateLens lens (modify ((+) 1))Lift a computation to work on a parent state when the child may be missing (Optional/AffineTraversal). The first argument runs if the child is missing; it uses the parent state.
Lift a computation to work on a custom type when focusing on one constructor via a Prism. The first argument runs if the constructor does not match.
Transform the error type. Useful when unifying error types from different subcomputations.
mapError (\e -> Wrapper e) (parseExpr ...)
A Reader Writer State Except monad stack. Useful when pure computations can fail—interpreters, compilers.
tellto log; messages are read at the end.Resultbut the API gives youthrow,catch, andtrywhich looks like imperative programming.Use RWSE over RWS when you need fail-fast error handling.