Funx.Monad.Maybe
Mix.install([
{:funx, "0.6.1"}
])
Overview
The Funx.Monad.Maybe module provides an implementation of the Maybe monad, a functional abstraction used to represent optional values in Elixir.
A Maybe represents one of two possibilities:
-
Just(value): the presence of a value -
Nothing: the absence of a value
This pattern is useful for eliminating nil checks and handling missing data explicitly and safely in functional pipelines.
Constructors
-
just/1: Wraps a value in theJustvariant. -
nothing/0: Returns aNothingvalue. -
pure/1: Alias forjust/1.
Refinement
-
just?/1: Returnstrueif the value is aJust. -
nothing?/1: Returnstrueif the value is aNothing.
Fallback and Extraction
-
get_or_else/2: Returns the value from aJust, or a default ifNothing. -
or_else/2: Returns the originalJust, or invokes a fallback function ifNothing. -
tap/2: Executes a side-effect function on aJustvalue, returning the originalMaybeunchanged.
List Operations
-
concat/1: Removes allNothingvalues and unwraps theJustvalues from a list. -
concat_map/2: Applies a function and collects onlyJustresults. -
sequence/1: Converts a list ofMaybevalues into a singleMaybeof list. -
traverse/2: Applies a function to each element in a list and sequences the results.
Lifting
-
lift_predicate/2: Converts a value toJustif it meets a predicate, otherwiseNothing. -
lift_identity/1: Converts anIdentityto aMaybe. -
lift_either/1: Converts anEitherto aMaybe. -
lift_eq/1: Lifts an equality function for use in theMaybecontext. -
lift_ord/1: Lifts an ordering function for use in theMaybecontext.
Elixir Interoperability
-
from_nil/1: ConvertsniltoNothing, otherwise wraps the value inJust. -
to_nil/1: Returns the underlying value ornil. -
from_result/1: Converts{:ok, val}or{:error, _}into aMaybe. -
to_result/1: Converts aMaybeto a result tuple. -
from_try/1: Runs a function and returnsJuston success, orNothingif an exception is raised. -
to_try!/2: Unwraps aJust, or raises an error ifNothing.
Protocols
The Just and Nothing structs implement the following protocols, making the Maybe abstraction composable and extensible:
-
Funx.Eq: Enables equality comparisons betweenMaybevalues. -
Funx.Foldable: Implementsfold_l/3andfold_r/3for reducing over the value or fallback. -
Funx.Filterable: Supports conditional retention withfilter/2,guard/2, andfilter_map/2. -
Funx.Monad: Providesmap/2,ap/2, andbind/2for monadic composition. -
Funx.Ord: Defines ordering behavior betweenJustandNothingvalues.
Although these implementations are defined per constructor (Just and Nothing), the behavior is consistent across the Maybe abstraction.
This module helps you represent optional data explicitly, structure conditional logic safely, and eliminate reliance on nil in functional pipelines.
Function Examples
import Funx.Monad.Maybe
alias Funx.Monad
alias Funx.Monad.Maybe
alias Funx.Tappable
just/1
Wraps a value in Just.
just(2)
nothing/0
Returns a Nothing value.
nothing()
pure/1
Alias for just/1.
pure(5)
Monad.map/2
Applies a function to the value inside a Just monad. If the Maybe is Nothing, it is returned unchanged.
This function is from the Funx.Monad module, not Funx.Monad.Maybe.
just(42)
|> Monad.map(&(&1 + 1))
nothing()
|> Monad.map(&(&1 + 1))
Monad.bind/2
Applies a function that returns a Maybe to the value inside a Just monad. This is also known as “flatMap” in other languages.
If the Maybe is Nothing, it is returned unchanged. If the function returns Nothing, that Nothing is returned.
This function is from the Funx.Monad module, not Funx.Monad.Maybe.
just(42)
|> Monad.bind(fn x -> just(div(x, 2)) end)
nothing()
|> Monad.bind(fn _ -> just(10) end)
just(42)
|> Monad.bind(fn _ -> nothing() end)
Monad.ap/2
Applies a function wrapped in a Maybe to a value wrapped in a Maybe.
If either the function or the value is Nothing, Nothing is returned.
This function is from the Funx.Monad module, not Funx.Monad.Maybe.
Monad.ap(just(&(&1 + 1)), just(42))
Monad.ap(nothing(), just(42))
Monad.ap(just(&(&1 + 1)), nothing())
just?/1
Returns true if the Maybe is Just, otherwise false.
just?(just(5))
just?(nothing())
nothing?/1
Returns true if the Maybe is Nothing, otherwise false.
nothing?(nothing())
nothing?(just(5))
get_or_else/2
Retrieves the value from a Maybe, returning default if Nothing.
get_or_else(just(5), 0)
get_or_else(nothing(), 0)
or_else/2
Returns the current Just value or invokes the fallback_fun if Nothing.
or_else(nothing(), fn -> just(42) end)
or_else(just(10), fn -> just(42) end)
tap/2
Executes a side-effect function on a Just value and returns the original Maybe unchanged.
If the Maybe is Nothing, the function is not called.
Useful for debugging, logging, or performing side effects without changing the value.
# Side effect on Just
just(42)
|> Tappable.tap(&IO.inspect(&1, label: "debug"))
# No side effect on Nothing
nothing()
|> Tappable.tap(&IO.inspect(&1, label: "debug"))
# In a pipeline
just(5)
|> Monad.map(&(&1 * 2))
|> Tappable.tap(&IO.inspect(&1, label: "after map"))
|> Monad.map(&(&1 + 1))
lift_eq/1
Lifts an equality function to compare Maybe values:
-
JustvsJust: Uses the custom equality function. -
NothingvsNothing: Alwaystrue. -
JustvsNothingor vice versa: Alwaysfalse.
eq = lift_eq(%{
eq?: fn x, y -> x == y end,
not_eq?: fn x, y -> x != y end
})
eq.eq?.(just(5), just(5))
eq.eq?.(just(5), just(10))
eq.eq?.(nothing(), nothing())
eq.eq?.(just(5), nothing())
lift_ord/1
Adapts an ordering function to compare Maybe values:
-
Nothingis considered less than anyJust. -
Two
Justvalues are compared by the provided function.
ord = lift_ord(%{
lt?: &/2,
ge?: &>=/2
})
ord.lt?.(just(3), just(5))
ord.lt?.(nothing(), just(5))
concat/1
Removes Nothing values from a list of Maybe and returns a list of unwrapped Just values.
concat([pure(1), nothing(), pure(2)])
concat([nothing(), nothing()])
concat([pure("a"), pure("b"), pure("c")])
concat_map/2
Maps a function over a list, collecting unwrapped Just values and ignoring Nothing in a single pass.
concat_map([1, 2, 3, 4], fn x ->
if rem(x, 2) == 0, do: pure(x), else: nothing()
end)
concat_map([1, nil, 3], fn
nil -> nothing()
x -> pure(x * 2)
end)
concat_map([1, 2, 3], fn x -> pure(x + 1) end)
concat_map([], fn x -> pure(x) end)
sequence/1
Converts a list of Maybe values into a Maybe containing a list. If any element is Nothing, the entire result is Nothing.
sequence([just(1), just(2)])
sequence([just(1), nothing()])
traverse/2
Applies a function to each element of a list, collecting results into a single Maybe. If any call returns Nothing, the operation halts and returns Nothing.
traverse([1, 2], fn x -> just(x * 2) end)
traverse([1, nil, 3], fn
nil -> nothing()
x -> just(x * 2)
end)
lift_identity/1
Converts an Identity value into a Maybe. If the value is nil, returns Nothing; otherwise Just.
lift_identity(Funx.Monad.Identity.pure(5))
lift_identity(Funx.Monad.Identity.pure(nil))
lift_either/1
Converts an Either to a Maybe. Right becomes Just, and Left becomes Nothing.
lift_either(Funx.Monad.Either.right(5))
lift_either(Funx.Monad.Either.left("Error"))
lift_predicate/2
Lifts a value into Maybe based on a predicate. If predicate.(value) is true, returns Just(value); otherwise Nothing.
lift_predicate(5, fn x -> x > 3 end)
lift_predicate(2, fn x -> x > 3 end)
to_predicate/1
Returns true if the given Maybe is a Just, or false if it is Nothing.
This provides a simple way to treat a Maybe as a boolean condition, useful when filtering or making branching decisions based on presence.
to_predicate(just(42))
to_predicate(nothing())
from_nil/1
Converts nil to Nothing; any other value becomes Just.
from_nil(nil)
from_nil(5)
to_nil/1
Converts a Maybe to its wrapped value or nil.
to_nil(just(5))
to_nil(nothing())
from_try/1
Executes a function within a Maybe context, returning Nothing if an exception occurs.
from_try(fn -> 5 end)
from_try(fn -> raise "error" end)
to_try!/2
Extracts a value from a Maybe, raising an exception if Nothing.
to_try!(just(5))
from_result/1
Converts a result tuple to a Maybe. {:ok, value} becomes Just(value), while {:error, _} becomes Nothing.
from_result({:ok, 5})
from_result({:error, :something})
to_result/1
Converts a Maybe to a result tuple. Just(value) becomes {:ok, value}, while Nothing becomes {:error, :nothing}.
to_result(just(5))
to_result(nothing())