Current section
Files
Jump to
Current section
Files
lib/monad/maybe/dsl/step.ex
defmodule Funx.Monad.Maybe.Dsl.Step do
@moduledoc false
# Step types for the Maybe DSL pipeline.
#
# Following Spark's Entity pattern, each step type is a distinct struct.
# This provides strong typing, clearer pattern matching, and compile-time guarantees.
#
# These are internal implementation details - the user-facing API remains unchanged.
defmodule Bind do
@moduledoc false
# Represents a bind operation in the pipeline.
#
# Bind is used for operations that return Maybe or result tuples.
@enforce_keys [:operation]
defstruct [:operation, :__meta__, opts: []]
@typedoc "A bind step that chains operations returning Maybe or result tuples"
@type t :: %__MODULE__{
operation: module() | function(),
opts: keyword(),
__meta__: map() | nil
}
end
defmodule Map do
@moduledoc false
# Represents a map operation in the pipeline.
#
# Map is used for pure transformations that return plain values.
@enforce_keys [:operation]
defstruct [:operation, :__meta__, opts: []]
@typedoc "A map step that transforms values with pure functions"
@type t :: %__MODULE__{
operation: module() | function(),
opts: keyword(),
__meta__: map() | nil
}
end
defmodule Ap do
@moduledoc false
# Represents an applicative functor operation.
#
# Ap applies a function wrapped in a Maybe to a value wrapped in a Maybe.
@enforce_keys [:applicative]
defstruct [:applicative, :__meta__]
@typedoc "An applicative step that applies wrapped functions to wrapped values"
@type t :: %__MODULE__{
applicative: term(),
__meta__: map() | nil
}
end
defmodule MaybeFunction do
@moduledoc false
# Represents a call to a Maybe-specific function.
#
# Maybe functions: or_else.
@enforce_keys [:function, :args]
defstruct [:function, :args, :__meta__]
@typedoc "A step calling a Maybe-specific function (or_else)"
@type t :: %__MODULE__{
function: atom(),
args: list(),
__meta__: map() | nil
}
end
defmodule ProtocolFunction do
@moduledoc false
# Represents a call to a Funx protocol function.
#
# Protocol functions are operations implemented via Elixir protocols rather than
# module functions. This allows the operation to work polymorphically across
# different types while maintaining a clean API.
#
# Examples:
# - tap (Funx.Tappable) - Execute side effects without changing the value
# - filter, guard, filter_map (Funx.Filterable) - Conditional retention
@enforce_keys [:protocol, :function, :args]
defstruct [:protocol, :function, :args, :__meta__]
@typedoc """
A step calling a protocol function.
The protocol module (e.g., Funx.Tappable) is stored explicitly so the executor
can dispatch to the correct protocol implementation.
"""
@type t :: %__MODULE__{
protocol: module(),
function: atom(),
args: list(),
__meta__: map() | nil
}
end
@typedoc """
Union type representing any Step type in the Maybe DSL pipeline.
Each step type is a distinct struct with enforced fields and type checking.
"""
@type t ::
Bind.t()
| Map.t()
| Ap.t()
| MaybeFunction.t()
| ProtocolFunction.t()
end