Functional programming utilities and monadic types.¶
This module provides functional programming constructs including monads, lazy evaluation, and caching utilities. It implements the monad pattern for composing computations in a functional style, with support for chaining operations and deferred evaluation.
Examples
Using Maybe for optional value handling:
from deluxe.functional import Maybe
def parse_int(value: str) -> Maybe[int]:
try:
return Maybe.pure(int(value))
except ValueError:
return Maybe()
result = parse_int("42")
match result:
case Maybe(value):
print(f"Parsed: {value}")
case _:
print("Invalid input")
Using Lazy for deferred computation:
from deluxe.functional import Lazy
def expensive_computation() -> int:
print("Computing...")
return 42
# Computation not executed yet
lazy_value = Lazy(expensive_computation, int)
# Computation executed here
result = lazy_value.unwrap() # Prints "Computing..."
Using MaybeCallable with enumerations:
from enum import Enum, member
from deluxe.functional import MaybeCallable
class Key(MaybeCallable[bytes]):
def __call__(self, *args: bytes) -> bytes:
return self._callable_(*args)
class KeyStroke(Key[bytes], Enum):
@member
@staticmethod
def Ctrl(key: bytes) -> str:
return f"C-{key.decode()}"
Space = b"Space"
See also
deluxe.types: Additional type utilities.deluxe.enums: Enhanced enumeration support.
Module Contents¶
Similar to property(), with the addition of… |
|
Protocol defining the interface for monadic types. |
|
Maybe class. |
|
Implementation of the |
|
Monad wrapping up a type or a callable… |
- class cached_property(func: collections.abc.Callable[[Any], _T_co])[source]¶
Extends:
Generic[_T_co]Similar to property(), with the addition of caching.
Transform a method of a class into a property whose value is computed once and then cached as a normal attribute for the life of the instance. Useful for expensive computed properties of instances that are otherwise effectively immutable.
Keys Differences from functools.cached_property:
support pure python class as well as mypyc compiled Native and Non-Native extension class.
prevent reset or deletion of the property on the instance
allow usage in class with or without writable __dict__ (eg., class with __slots__)
- func : collections.abc.Callable[[Any], _T_co]¶
- class Monad[source]¶
Extends:
Protocol[_T]Protocol defining the interface for monadic types.
A monad is a design pattern from functional programming that provides a way to structure computations. It defines a standard interface for values that can be “wrapped” in a context, enabling consistent handling of computations that might involve side effects, error handling, or delayed evaluation.
The protocol requires implementations to provide methods for wrapping values in the monadic context (
pure()), transforming wrapped values (map()), and chaining computations that produce monadic results (bind()).Examples
Basic usage with the
Lazyimplementation:from deluxe.functional import Lazy # Wrap a value using pure lazy_value = Lazy.pure(42) # Transform the value using map lazy_doubled = lazy_value.map(lambda x: x * 2) print(lazy_doubled.unwrap()) # 84 # Chain computations using bind def to_lazy(x: int) -> Lazy[str]: return Lazy.pure(str(x)) lazy_string = lazy_value.bind(to_lazy) print(lazy_string.unwrap()) # "42"- Protocol Methods:
pure(): Wrap a plain value in the monadic context.map(): Apply a function to the wrapped value (functorial map).bind(): Chain a function that returns cast(_OST, a monadic value.)join(): Flatten a monad of monads into a single monad.unwrap(): Extract the wrapped value from the monadic context.__call__(): Alias forunwrap(), allowing monads to be called.
See also
- classmethod pure(value: _T) Self[source]¶
Wrap a plain value into the monadic context.
This is a class method that creates a new monadic instance containing the given plain value. This is the primary way to enter the monadic context from a non-monadic value.
- map(func: collections.abc.Callable[[_T], _U], *args: Any, **kwds: Any) Monad[_U][source]¶
Apply a function to the wrapped value (functorial map).
This method transforms the value inside the monadic context by applying the given function. It preserves the monadic structure, returning a new monad containing the transformed value.
The
mapmethod implements the functor pattern, which is typically derived frombind()andpure()in functional programming theory.
- bind(func: collections.abc.Callable[[_T], Monad[_U]], *args: Any, **kwds: Any) Monad[_U][source]¶
Chain a function that returns a monadic value.
This method allows for chaining computations that produce monadic results. It applies the function to the wrapped value, which itself returns a monad, and then flattens the result to avoid nested monads.
The
bindoperation (also known asflatMapor>>=in other languages) is the fundamental operation that gives monads their power for sequencing computations.
- join() Self[source]¶
Flatten a monad of monads into a single monad.
This method unwraps one level of monadic structure, which is useful when dealing with nested monadic contexts. For monads wrapping other monads of the same type,
joinprovides a way to reduce the nesting level.The
joinoperation is related tobindand is defined as:m.join() == m.bind(lambda x: x).- Returns:¶
A monadic instance with one level of nesting removed.
- unwrap() _T[source]¶
Extract the wrapped value from the monadic context.
This method returns the plain value contained within the monadic structure. This operation is sometimes called
runorvaluein other monad implementations.Note that not all monads support this operation, as some monadic contexts (like IO monads) are meant to remain encapsulated to maintain referential transparency.
- Returns:¶
The plain value that was wrapped in the monadic context.
-
class Maybe(value: _T =
Unset)[source]¶ Extends:
Generic[_T]Maybe class.
Usage¶
Note
For pattern matching against
deluxe.types.Unsetyou can’t use Maybe(Unset), this won’t work at runtime nor statically with typechecker. You should instead import thedeluxe.typesmodule and use Maybe(types.Unset).- classmethod pure(value: _T) Self[source]¶
Wrap a plain value into the
Maybemonadic context.Creates a new
Maybeinstance containing the given value. This is the primary way to enter theMaybecontext from a non-monadic value.- Parameters:¶
- Returns:¶
A
Maybeinstance containing the wrappedvalue.- Raises:¶
ValueError – If
valueisdeluxe.types.Unset.
Examples:
>>> from deluxe.functional import Maybe >>> Maybe.pure(42) Maybe[int](42) >>> Maybe.pure("hello") Maybe[str](hello)
- map(func: collections.abc.Callable[[_T], _U], *_args: Any, **_kwds: Any) Maybe[_U][source]¶
Apply a function to the wrapped value (functorial map).
This method transforms the value inside the
Maybecontext by applying the given function. It preserves the monadic structure, returning a newMaybecontaining the transformed value.If this
Maybeis empty (containsdeluxe.types.Unset), the function is not called and an emptyMaybeis returned.- Parameters:¶
- func: collections.abc.Callable[[_T], _U]¶
A callable that receives the wrapped value and returns a transformed value of a (potentially different) type.
- Returns:¶
A new
Maybewrapping the result offunc, whose type matches the return type offunc. Returns an emptyMaybeif this instance is empty.
Examples:
>>> from deluxe.functional import Maybe >>> Maybe.pure(5).map(lambda x: x * 2) Maybe[int](10) >>> Maybe().map(lambda x: x * 2) Maybe[NoneType](Unset)
- bind(func: collections.abc.Callable[[_T], Maybe[_U]], *_args: Any, **_kwds: Any) Maybe[_U][source]¶
Chain a function that returns a
Maybevalue.This method allows for chaining computations that produce
Mayberesults. It applies the function to the wrapped value, which itself returns aMaybe, and then flattens the result to avoid nested monads.If this
Maybeis empty (containsdeluxe.types.Unset), the function is not called and an emptyMaybeis returned.The
bindoperation (also known asflatMapor>>=in other languages) is the fundamental operation that gives monads their power for sequencing computations.- Parameters:¶
- func: collections.abc.Callable[[_T], Maybe[_U]]¶
A callable that receives the wrapped value and returns a
Maybewrapping a value of a (potentially different) type.
- Returns:¶
A new
Maybewrapping the result offunc, whose type matches the return type offunc, with the nestedMaybestructure flattened.
Examples:
>>> from deluxe.functional import Maybe >>> def double_if_positive(x: int) -> Maybe[int]: ... return Maybe(x * 2) if x > 0 else Maybe() >>> Maybe.pure(5).bind(double_if_positive) Maybe[int](10) >>> Maybe.pure(-1).bind(double_if_positive) Maybe[NoneType](Unset)
- join() Self[source]¶
Flatten a nested
Maybeinto a singleMaybe.This method unwraps one level of monadic structure, which is useful when dealing with nested
Maybecontexts. It extracts the value and re-wraps it in a freshMaybe.The
joinoperation is related tobindand is defined as:m.join() == m.bind(lambda x: x).Examples:
>>> from deluxe.functional import Maybe >>> nested = Maybe(Maybe(42)) >>> nested.join() Maybe[int](42)
- unwrap() _T[source]¶
Extract the wrapped value from the
Maybecontext.This method returns the plain value contained within the monadic structure. If this
Maybeis empty, it returnsdeluxe.types.Unset.- Returns:¶
The plain value of type
_Tthat was wrapped in theMaybecontext, ordeluxe.types.Unsetif empty.
Examples:
>>> from deluxe.functional import Maybe >>> Maybe.pure(42).unwrap() 42 >>> Maybe().unwrap() Unset
- class Lazy(value: collections.abc.Callable[[], _T], type: Lazy.__init__.type[_T])[source]¶
Extends:
Generic[_T]Implementation of the
Monadprotocol for lazy evaluation.The
Lazyclass wraps a callable that defers computation until explicitly requested. This enables lazy evaluation, where computations are only performed when their results are needed, and results are cached across multiple accesses.The
__set_name__()method is present solely to allow usage ofLazyas enumeration’s member indeluxe.enums.Enum.Examples
Creating a lazy value from a callable:
from deluxe.functional import Lazy def expensive_computation() -> int: print("Computing...") return 42 lazy_value = Lazy(expensive_computation, int) # Nothing printed yet - computation not executed result = lazy_value.unwrap() # Prints "Computing..." # result is now 42 result2 = lazy_value.unwrap() # Prints again - result is not cachedUsing
pure()to wrap static values:from deluxe.functional import Lazy lazy_int = Lazy.pure(42) print(lazy_int.unwrap()) # 42Chaining transformations with
map():from deluxe.functional import Lazy lazy_value = Lazy.pure(5) lazy_doubled = lazy_value.map(lambda x: x * 2, int) lazy_squared = lazy_doubled.map(lambda x: x * x, int) print(lazy_squared.unwrap()) # 100Chaining monadic computations with
bind():from deluxe.functional import Lazy def parse_int(s: str) -> Lazy[int]: return Lazy.pure(int(s)) lazy_str = Lazy.pure("42") lazy_int = lazy_str.bind(parse_int, int) print(lazy_int.unwrap()) # 42Calling lazy values as functions:
from deluxe.functional import Lazy lazy_value = Lazy.pure(10) result = lazy_value() # Equivalent to lazy_value.unwrap() print(result) # 10Note
While this class has a
__set_name__()method, it is not a descriptor. The method is used only to track metadata (__objclass__and_name_) when instances are assigned as class attributes. To access the value, explicitly callunwrap()or use the callable syntaxinstance().- Parameters:¶
- value: collections.abc.Callable[[], _T]¶
A callable (typically a function or lambda) that will be executed when the lazy value is unwrapped.
- type: Lazy.__init__.type[_T]¶
The type of the value that will be produced when
valueis called. This is used for type annotations and can be different from the callable’s actual return type.
Note
The
valuecallable is not executed during initialization. The computation is deferred untilunwrap()or__call__()is invoked.See also
Monad: The protocol that this class implements.
- property type : type[_T]¶
Return the Python type that this
Lazyinstance will produce.- Returns:¶
The type annotation of the value that will be produced when this lazy computation is evaluated.
Examples
>>> from deluxe.functional import Lazy >>> lazy_int = Lazy(lambda: 42, int) >>> lazy_int.type <class 'int'>
- classmethod pure(value: _U) Lazy[_U][source]¶
Wrap a plain value into the lazy monadic context.
Creates a
Lazyinstance that will always return the given static value when unwrapped. This is the primary way to create a lazy value from a non-callable.- Parameters:¶
- value: _U¶
The plain value to wrap in the lazy context.
- Returns:¶
A
Lazyinstance that will producevaluewhen unwrapped.
Examples
>>> from deluxe.functional import Lazy >>> lazy_int = Lazy.pure(42) >>> lazy_int.unwrap() 42
- map(func: collections.abc.Callable[[_T], _U], type: Lazy.map.type[_U]) Lazy[_U][source]¶
Apply a function to the lazy value using functorial map.
Creates a new
Lazyinstance that will applyfuncto the result of this lazy computation when unwrapped. This allows for chaining transformations in a lazy context.- Parameters:¶
- func: collections.abc.Callable[[_T], _U]¶
A callable that receives the unwrapped value and returns a transformed value of a (potentially different) type.
- type: Lazy.map.type[_U]¶
The type of the value that
funcwill return.
- Returns:¶
A new
Lazyinstance that will producefunc(value)when unwrapped, wherevalueis the result of unwrapping this instance.
Examples
>>> from deluxe.functional import Lazy >>> lazy_value = Lazy.pure(5) >>> lazy_doubled = lazy_value.map(lambda x: x * 2, int) >>> lazy_doubled.unwrap() 10
- bind(func: collections.abc.Callable[[_T], Lazy[_U]], type: Lazy.bind.type[_U]) Lazy[_U][source]¶
Chain a function that returns a lazy value using monadic bind.
Applies
functo the result of this lazy computation and returns the resultingLazyinstance directly. This allows for chaining computations where each step produces a new lazy context.- Parameters:¶
- Returns:¶
The
Lazyinstance returned byfunc(value), wherevalueis the result of unwrapping this instance.
Examples
>>> from deluxe.functional import Lazy >>> def parse_int(s: str) -> Lazy[int]: ... return Lazy.pure(int(s)) >>> lazy_str = Lazy.pure("42") >>> lazy_int = lazy_str.bind(parse_int, int) >>> lazy_int.unwrap() 42
- join() Self[source]¶
Flatten this lazy value by wrapping it again.
Creates a new
Lazyinstance that will produce the result of unwrapping this instance. This is useful for normalizing lazy values and ensuring a consistent lazy context.Examples
>>> from deluxe.functional import Lazy >>> lazy_value = Lazy.pure(42) >>> lazy_joined = lazy_value.join() >>> lazy_joined.unwrap() 42
- unwrap() _T[source]¶
Execute the lazy computation and return its result.
This method invokes the wrapped callable and returns its result. The callable is executed each time
unwrap()is called; results are not automatically cached.- Returns:¶
The result of executing the wrapped callable of type
_T.
Examples
>>> from deluxe.functional import Lazy >>> def compute() -> int: ... print("Computing...") ... return 42 >>> lazy_value = Lazy(compute, int) >>> result = lazy_value.unwrap() Computing... >>> print(result) 42Note
lazy_value() is equivalent to lazy_value.unwrap()
- class MaybeCallable(value: _T | collections.abc.Callable[[_T], _T])[source]¶
Extends:
Generic[_T]Monad wrapping up a type or a callable returning this same type.
The
MaybeCallabletype is designed to represent values that can be either plain values or callables that produce values of the same type. This is particularly useful when defining enumerations where some members are simple values while others are callable (factory) members.When used as a base class for enumerations,
MaybeCallableenables a hybrid pattern where enum members can be either:Plain values: Direct values of the wrapped type.
Callable members: Methods decorated with
@memberthat accept arguments and return values of the wrapped type.
Examples
Basic usage with plain values and callables:
from deluxe.functional import MaybeCallable # Wrapping a plain value plain = MaybeCallable(42) print(plain.unwrap()) # 42 # Wrapping a callable def double(x: int) -> int: return x * 2 callable_val = MaybeCallable(double) print(callable_val(21)) # 42Usage as an enumeration base class:
from enum import Enum, member from deluxe.enums import Enum as DocEnum from deluxe.functional import MaybeCallable class Key(MaybeCallable[bytes]): def __call__(self, *args: bytes) -> bytes: return self._callable_(*args) class KeyStroke(Key[bytes], Enum): @staticmethod def _join(string: bytes, other: bytes) -> str: return string.decode() + other.decode() @member @staticmethod def Ctrl(key: bytes) -> str: return KeyStroke._join(b"C-", key) @member @staticmethod def Meta(key: bytes) -> str: return KeyStroke._join(b"M-", key) Space = b"Space" Tab = b"Tab" Enter = b"Enter" # Plain enum members print(KeyStroke.Space) # b"Space" print(KeyStroke.Tab) # b"Tab" # Callable enum members print(KeyStroke.Ctrl(b"h")) # "C-h" print(KeyStroke.Meta(b"a")) # "M-a"Another example with byte strings:
from deluxe.functional import MaybeCallable from deluxe.enums import Enum as DocEnum class BString(MaybeCallable[bytes]): @staticmethod def format(value: bytes) -> bytes: return b"".join((b"#{", value, b"}")) def map(self, func): return self.pure(func(self.unwrap())) class Format(BString, DocEnum): @member @staticmethod def str(string: bytes) -> bytes: return BString(string) @member @staticmethod def var(string: bytes) -> bytes: name = string.decode() return getattr(Enum("Format", ((name, string),), type=BString), name) active_window_index = b"active_window_index" pane_id = b"pane_id" # Plain enum members print(Format.active_window_index) # b"active_window_index" # Callable enum members print(Format.var(b"opt")) # creates a new Format memberNote
When using
MaybeCallableas a base for enumerations, the__call__method should be overridden in the subclass to properly handle callable enum members. The@memberdecorator fromenumis used to mark static methods as callable enum members.- Parameters:¶
- value: _T | collections.abc.Callable[[_T], _T]¶
Either a plain value or a callable that accepts no arguments and returns a value of the same type.
See also
Lazy: A monad for lazy evaluation of computations.Maybe: A monad representing optional values.deluxe.enums.Enum: Enhanced enumeration base class.
- classmethod pure(value: _T | collections.abc.Callable[[_T], _T]) Self[source]¶
Wrap a plain value or callable into the monadic context.
Creates a new
MaybeCallableinstance containing the given value. This is the primary way to enter theMaybeCallablecontext from a non-monadic value.- Parameters:¶
- value: _T | collections.abc.Callable[[_T], _T]¶
A plain value or a callable that returns a value.
- Returns:¶
A new
MaybeCallablewrapping the given value.
- map(func: collections.abc.Callable[[_T | collections.abc.Callable[[_T], _T]], _T]) Self[source]¶
Apply a function to the wrapped value (functorial map).
This method transforms the value inside the
MaybeCallablecontext by applying the given function. It preserves the monadic structure, returning a newMaybeCallablecontaining the transformed value.- Parameters:¶
- func: collections.abc.Callable[[_T | collections.abc.Callable[[_T], _T]], _T]¶
A callable that receives the wrapped value and returns a transformed value of a (potentially different) type.
- Returns:¶
A new
MaybeCallablewrapping the result offunc, whose type matches the return type offunc.
- bind(func: collections.abc.Callable[[_T | collections.abc.Callable[[_T], _T]], Self]) Self[source]¶
Chain a function that returns a
MaybeCallablevalue.This method allows for chaining computations that produce
MaybeCallableresults. It applies the function to the wrapped value, which itself returns aMaybeCallable, and then flattens the result to avoid nested monads.The
bindoperation (also known asflatMapor>>=in other languages) is the fundamental operation that gives monads their power for sequencing computations.- Parameters:¶
- func: collections.abc.Callable[[_T | collections.abc.Callable[[_T], _T]], Self]¶
A callable that receives the wrapped value and returns a
MaybeCallablewrapping a value of a (potentially different) type.
- Returns:¶
A new
MaybeCallablewrapping the result offunc, whose type matches the return type offunc, with the nestedMaybeCallablestructure flattened.
- join() Self[source]¶
Flatten a nested
MaybeCallableinto a single instance.This method unwraps one level of monadic structure, which is useful when dealing with nested
MaybeCallablecontexts. It extracts the value and re-wraps it in a freshMaybeCallable.The
joinoperation is related tobindand is defined as:m.join() == m.bind(lambda x: x).- Returns:¶
A
MaybeCallableinstance with one level of nesting removed.
- unwrap() _T[source]¶
Extract the plain wrapped value from the monadic context.
This method returns the plain value contained within the monadic structure. Raises an error if the wrapped value is a callable rather than a plain value.
- Returns:¶
The plain value that was wrapped in the
MaybeCallablecontext.- Raises:¶
TypeError – If the wrapped value is a callable.