A Scheme take on the environment (reader) monad, focusing more on being efficient and convenient than pure.
Define a new environment monad. This syntax hides the implementation, allowing the use of records, dynamic parameters, or explicit value passing. Thename
is used for description and may or may not be bound
to a value representing the monad. All other parameters are
keywords, and with the exception of fields
simply provide
binding names for the monad operators described below.
The fields:
keyword takes a list of field name
identifiers known to be used by the monad. This is an
optimization hint, as the monad can be used to store and query
values for any identifier at runtime.
The following keywords obey the definition of a monad:
sequence: sequence (>>) - Essentially a semi-colon, this joins two
operations together.
bind: (>>=) - Runs a normal function. As a syntactic convenience,
bind
looks and behaves like a lambda, but the parameters
of the bind
are bound as Scheme variables with the
values of the corresponding environment variables. Thus you
fetch the values of foo and bar with:
(bind (foo bar) ...)
hiding the need for an explicit ask
. If you want to
bind the values to some other name, you can use it like a
let
:
(bind ((my-foo foo) (my-bar bar)) ...)
return: Returns a pure (non-monadic) value.
run: Start the monad.
The following are specific to the environment monad:
ask: Ask the current value of an environment variable. This is not
meant to be used directly - use the `bind' syntax to query bindings.
local: Shadow the value one or more environment variables,
analogous to `let'.
In addition, support for optional mutation is provided:
local!: (local! (var val) ...) will update the environment with
the corresponding variable bindings. In a sequence, successive
operations will see the result of the update, unlike with `local'.
This is allowed, but not required, to perform mutation.
bind-fork: (bind-fork a b)
runs `a' followed by `b',
passing `b' the original state before `a' was run.