This library provides ways to compose functions of the type
s -> (a, s)
. This composition threads state through a computation
From time to time, you'll see a pattern like this in your code
( newValue, newState ) =
f state
( newerValue, newerState ) =
g newValue newState
( newererValue, newererState ) =
h newerValue newerState
This pattern is ugly and error-prone (because of typo's, for instance).
It can be abstracted by creating a function that composes f
and g
(
the output of f
is the input to g
).
f : s -> ( a, s )
g : a -> s -> ( a, s )
This library implements this composition and provides a bunch of helper functions for working with State. For a more in-depth explanation of how the implementation works, see the derivation. For more detailed, higher level documentation, please see the readme and the examples
Type that represents state.
Note that State
wraps a function, not a concrete value.
state : value -> State state value
Create a new State from a value of any type.
embed : (s -> a) -> State s a
Embed a function into State. The function is applied to the state, the result will become the value.
It is implemented as:
embed : (a -> b) -> State a b
embed f =
State (\s -> ( f s, s ))
This function can be extended as follows:
embed2 : (a -> b -> c) -> a -> State b c
embed2 f arg1 =
embed (f arg1)
map : (a -> b) -> State s a -> State s b
Apply a function to the value that the state holds
map2 : (a -> b -> c) -> State s a -> State s b -> State s c
Apply a function to the value of two states. The newest state will be kept
andMap : State s a -> State s (a -> b) -> State s b
Apply a function wrapped in a state to a value wrapped in a state. This is very useful for applying stateful arguments one by one.
The use of andMap
can be substituted by using mapN. The following
expressions are equivalent.
map f arg1 |> andMap arg2 == State.map2 f arg1 arg2
In general, using the mapN
functions is preferable. The mapN
functions can
be defined up to an arbitrary n
using andMap
.
State.mapN f arg1 arg2 ... argN
== State.map f arg1
|> andMap arg2
...
|> andMap argN
andThen : (a -> State s b) -> State s a -> State s b
Chain two operations with state.
The readme has a section on structuring computation
with andThen
.
get : State s s
Get the current state. Typically the state is modified somehow and then put back with put.
put : s -> State s ()
Replace the current state with a new one.
run : s -> State s a -> ( a, s )
Thread the state through a computation, and return both the final state and the computed value
Note for Haskellers: the argument order is swapped. This is more
natural in elm because code is often structured left to right using (|>)
.
finalValue : s -> State s a -> a
Thread the state through a computation, and return only the computed value
fibs : List Int -> List Int
fibs =
let
initialState =
Dict.fromList [ ( 0, 1 ), ( 1, 1 ) ]
in
State.finalValue initialState << fibsHelper
-- fibsHelper : List Int -> State (Dict Int Int) (List Int)
See Fibonacci.elm for the full example.
traverse : (a -> State s b) -> List a -> State s (List b)
Generalize List.map
to work with State
.
When you have a function the works on a single element,
mark : Int -> State (Array Bool) ()
mark index =
State.modify (Array.set index False)
traverse can be used to let it work on a list of elements, taking care of threading the state through.
markMany : List Int -> State (Array Bool) (List ())
markMany =
State.traverse mark
This function is also called mapM
.
combine : List (State s a) -> State s (List a)
Combine a list of State
s into one by composition.
The resulting value is a list of the results of subcomputations.
filterM : (a -> State s Basics.Bool) -> List a -> State s (List a)
Generalize List.filter
to work on State
. Composes only the states that satisfy the predicate.
-- keep only items that occur at least once
[ 1, 2, 3, 4, 4, 5, 5, 1 ]
|> State.filter (\element -> State.advance (\cache -> (List.member element cache, element :: cache)))
|> State.run []
--> ([4,5,1], [1,5,5,4,4,3,2,1])
foldlM : (b -> a -> State s b) -> b -> List a -> State s b
Compose a list of updated states into one from the left. Also called foldM
.
tailRec : (a -> Step a b) -> a -> b
Create a pure tail-recursive function of one argument
pow : number -> Int -> number
pow n p =
let
go { accum, power } =
if power == 0 then
Done accum
else
Loop
{ accum = accum * n
, power = power - 1
}
in
tailRec go { accum = 1, power = p }
tailRecM : (a -> State s (Step a b)) -> a -> State s b
The tailRecM
function takes a step function and applies it recursively until
a pure value of type b
is found. Because of tail recursion, this function runs in constant stack-space.
{-| Perform an action n times, gathering the results
-}
replicateM : Int -> State s a -> State s (List a)
replicateM n s =
let
go ( n, xs ) =
if n < 1 then
state (Done xs)
else
map (\x -> Loop ( n - 1, x :: xs )) s
in
tailRecM go ( n, [] )