Syntax Sugar Module¶
Syntactic sugar for monadic operations.
This module provides convenient syntax for working with monads, making monadic code more readable and maintainable.
The do() decorator provides Haskell-style do-notation for sequencing
monadic computations using generator functions.
Do Notation¶
- katharos.syntax_sugar.do(monad_type)[source]¶
Decorator for generator-based do-notation.
Each
yield monadextracts the wrapped value, analogous to<-in Haskell. The unwrapped value is immediately available for use in subsequent yields.Annotate the generator with
DoBlock[R]whereRis the plain return type. Annotate individual yield sites inline for per-binding types:x: int = yield Maybe.Just(3) y: str = yield Maybe.Just("hi")
A plain
return valueis automatically lifted viamonad_type.ret(). Returning an already-monadic value passes it through unchanged.Short-circuiting is handled transparently by
bind: if any yielded monad isNothingorFailure, the rest of the generator is abandoned and that monad is returned immediately.Works correctly with non-deterministic monads such as
ImmutableListwhosebindcalls its function more than once. Each branch replays the generator from the start with the accumulated history of sent values, so every execution path sees a fresh generator in the right state.Note
Side effects in the generator body (between
yieldexpressions) will be replayed once per branch. Keep the generator body pure; place any side effects inside the yielded monads themselves.- Parameters:
monad_type (
type[TypeVar(M, bound=Monad)]) – The concrete monad class (e.g.Maybe,Result).- Return type:
Callable[[Callable[...,GenericAlias[TypeVar(R)]]],Callable[...,TypeVar(M, bound=Monad)]]- Returns:
A decorator that transforms a generator function into an ordinary function returning a value of type
M.
Examples
>>> from katharos.types import Maybe >>> @do(Maybe) ... def computation() -> DoBlock[int]: ... x: int = yield Maybe.Just(3) ... y: int = yield Maybe.Just(x + 1) # x is 3 here ... return x + y >>> computation() Just(7)
>>> @do(Maybe) ... def short_circuit() -> DoBlock[int]: ... x: int = yield Maybe.Just(10) ... y: int = yield Maybe.Nothing() # stops here ... return x + y # never reached >>> short_circuit() Nothing()
- katharos.syntax_sugar.DoBlock = DoBlock¶
Type alias.
Type aliases are created through the type statement:
type Alias = int
In this example, Alias and int will be treated equivalently by static type checkers.
At runtime, Alias is an instance of TypeAliasType. The __name__ attribute holds the name of the type alias. The value of the type alias is stored in the __value__ attribute. It is evaluated lazily, so the value is computed only if the attribute is accessed.
Type aliases can also be generic:
type ListOrSet[T] = list[T] | set[T]
In this case, the type parameters of the alias are stored in the __type_params__ attribute.
See PEP 695 for more information.