Current section

Files

Jump to
funx lib monad maybe nothing.ex
Raw

lib/monad/maybe/nothing.ex

defmodule Funx.Monad.Maybe.Nothing do
@moduledoc """
Represents the `Nothing` variant of the `Maybe` monad, used to model the absence of a value.
A `Nothing` indicates that no value is present. All operations in the monad context simply propagate the absence, making `Nothing` an identity for failure or emptiness.
This module implements the following protocols:
- `Funx.Monad`: Implements `bind/2`, `map/2`, and `ap/2`, all of which return `Nothing`.
- `Funx.Foldable`: Provides `fold_l/3` and `fold_r/3`, invoking the fallback function when folding.
- `Funx.Filterable`: Supports filtering operations, which always return `Nothing`.
- `Funx.Eq`: Enables equality checks between `Nothing` and other `Maybe` values.
- `Funx.Ord`: Defines ordering behavior between `Nothing` and `Just`.
These implementations ensure that `Nothing` behaves consistently in functional composition, filtering, and comparison, treating absence as a stable and composable case.
"""
defstruct []
@type t :: %__MODULE__{}
@doc """
Creates a new `Nothing` value.
## Examples
iex> Funx.Monad.Maybe.Nothing.pure()
%Funx.Monad.Maybe.Nothing{}
"""
@spec pure() :: t()
def pure, do: %__MODULE__{}
end
defimpl String.Chars, for: Funx.Monad.Maybe.Nothing do
alias Funx.Monad.Maybe.Nothing
def to_string(%Nothing{}), do: "Nothing"
end
defimpl Funx.Monad, for: Funx.Monad.Maybe.Nothing do
alias Funx.Monad.Maybe.Nothing
@spec map(Nothing.t(), (term() -> term())) :: Nothing.t()
def map(%Nothing{}, _func), do: %Nothing{}
@spec ap(Nothing.t(), Nothing.t()) :: Nothing.t()
def ap(%Nothing{}, _val), do: %Nothing{}
@spec bind(Nothing.t(), (term() -> Nothing.t())) :: Nothing.t()
def bind(%Nothing{}, _func), do: %Nothing{}
end
defimpl Funx.Foldable, for: Funx.Monad.Maybe.Nothing do
alias Funx.Monad.Maybe.Nothing
def fold_l(%Nothing{}, _just_func, nothing_func) do
nothing_func.()
end
def fold_r(%Nothing{} = nothing, just_func, nothing_func) do
fold_l(nothing, just_func, nothing_func)
end
end
defimpl Funx.Filterable, for: Funx.Monad.Maybe.Nothing do
alias Funx.Monad.Maybe.Nothing
@spec guard(Funx.Monad.Maybe.Nothing.t(), boolean()) :: Funx.Monad.Maybe.t(any())
def guard(%Nothing{}, _boolean), do: %Nothing{}
@spec filter(Funx.Monad.Maybe.Nothing.t(), (any() -> boolean())) :: Funx.Monad.Maybe.t(any())
def filter(%Nothing{}, _predicate), do: %Nothing{}
@spec filter_map(Funx.Monad.Maybe.Nothing.t(), (any() -> Funx.Monad.Maybe.t(any()))) ::
Funx.Monad.Maybe.Nothing.t()
def filter_map(%Nothing{}, _func), do: %Nothing{}
end
defimpl Funx.Eq, for: Funx.Monad.Maybe.Nothing do
alias Funx.Monad.Maybe.{Nothing, Just}
def eq?(%Nothing{}, %Nothing{}), do: true
def eq?(%Nothing{}, %Just{}), do: false
def not_eq?(%Nothing{}, %Nothing{}), do: false
def not_eq?(%Nothing{}, %Just{}), do: true
end
defimpl Funx.Ord, for: Funx.Monad.Maybe.Nothing do
alias Funx.Monad.Maybe.{Nothing, Just}
def lt?(%Nothing{}, %Just{}), do: true
def lt?(%Nothing{}, %Nothing{}), do: false
def le?(%Nothing{}, %Just{}), do: true
def le?(%Nothing{}, %Nothing{}), do: true
def gt?(%Nothing{}, %Just{}), do: false
def gt?(%Nothing{}, %Nothing{}), do: false
def ge?(%Nothing{}, %Just{}), do: false
def ge?(%Nothing{}, %Nothing{}), do: true
end
defimpl Funx.Summarizable, for: Funx.Monad.Maybe.Nothing do
def summarize(_), do: {:maybe_nothing, Funx.Summarizable.summarize(nil)}
end