Maybe
View SourceThe Maybe DSL is a pipeline DSL that executes a sequence of operations on an input value. See the DSL Overview for the distinction between builder and pipeline DSLs.
Structure
A maybe block compiles to a struct containing the pipeline input, ordered steps, return mode (:maybe, :nil, or :raise), and user-supplied options. This struct is the complete representation of the DSL expression and is what the executor receives at runtime.
Steps
The Maybe DSL uses a small set of step types, each represented by its own struct:
Step.BindStep.MapStep.ApStep.MaybeFunctionStep.ProtocolFunction
Each step describes a single operation. The executor pattern-matches on these structs to determine how the pipeline proceeds.
Pipeline
├── Step.Bind
├── Step.Map
├── Step.Ap
├── Step.MaybeFunction
└── Step.ProtocolFunctionParser
The parser converts the DSL block into a step list. It applies the Maybe DSL's lifting rules (turning call forms into unary functions), expands module aliases, validates operations, and raises compile-time errors for unsupported syntax. The parser produces the final step list that appears in the compiled struct.
Transformers
Transformers run during compilation and may rewrite the step list before it is finalized. They can add, remove, or rearrange steps. A transformer must return a valid list of Maybe step structs and introduces a compile-time dependency for modules that use it.
Execution
The executor evaluates steps in order:
Step.Bindunpacks the current Maybe value, calls the operation, and normalizes its return into Maybe (accepting Maybe, Either, result tuples, or nil).Step.Mapapplies a pure function to the inner value.Step.Apapplies an applicative function contained in a Maybe.Step.MaybeFunctioncalls a built-in Maybe operation such asor_else.Step.ProtocolFunctioncalls a protocol operation such astap(Funx.Tappable),filter,filter_map, orguard(Funx.Filterable).
A Nothing value stops the pipeline immediately. The return mode controls how the final result is wrapped (:maybe returns the Maybe struct, :nil unwraps to the value or nil, :raise unwraps or raises an error).
Behaviours
Modules participating in the Maybe DSL implement specific monad behaviors based on their purpose:
Funx.Monad.Behaviour.Bind- operations that can fail (called withbind/3,tap/3,filter_map/3)Funx.Monad.Behaviour.Map- pure transformations (called withmap/3)Funx.Monad.Behaviour.Predicate- boolean tests (called withpredicate/3forfilterandguard)Funx.Monad.Behaviour.Ap- applicative functors (called withap/3)
The executor calls the appropriate behavior method based on the DSL operation. Each behavior has specific semantics for how the result is interpreted and processed in the pipeline.