### Install pre-commit hooks Source: https://github.com/dbrattli/expression/blob/main/README.md Run this command in the repository root to install the pre-commit hooks, which run code checks on changed files every time you commit. Requires pre-commit to be installed. ```bash > pre-commit install ``` -------------------------------- ### Install Expression via pip Source: https://github.com/dbrattli/expression/blob/main/README.md Install the core Expression package from PyPI using pip or pip3. Requires Python 3.10 or later. ```console > pip install expression ``` -------------------------------- ### Install Expression with Pydantic v2 support Source: https://github.com/dbrattli/expression/blob/main/README.md Install the optional 'pydantic' extra to enable Pydantic v2 support for Expression types. ```console > pip install expression[pydantic] ``` -------------------------------- ### Sequence transforming pipeline with Seq and seq Source: https://github.com/dbrattli/expression/blob/main/README.md Shows how to create a sequence transforming pipeline using Seq.of and the seq module's map, filter, and fold functions. The example defines mapper, predicate, and folder functions, then pipes a sequence through them and asserts the result. ```python from collections.abc import Callable from expression.collections import Seq, seq # Since static type checkes aren't good good at inferring lambda types mapper: Callable[[int], int] = lambda x: x * 10 predicate: Callable[[int], bool] = lambda x: x > 100 folder: Callable[[int, int], int] = lambda s, x: s + x xs = Seq.of(9, 10, 11) ys = xs.pipe( seq.map(mapper), seq.filter(predicate), seq.fold(folder, 0), ) assert ys == 110 ``` -------------------------------- ### AsyncOption with effect.async_option Source: https://github.com/dbrattli/expression/blob/main/README.md Example of using the AsyncOption type with the effect.async_option decorator for asynchronous optional values. It shows short-circuiting on Nothing and how to yield and await values. ```python from collections.abc import AsyncGenerator from expression import Nothing, Some, effect @effect.async_option[int]() async def fn_option() -> AsyncGenerator[int, int]: x: int = yield 42 # Regular value y: int = yield await Some(43) # Awaitable Some value # Short-circuit if condition is met if x + y > 80: z: int = yield await Nothing # This will short-circuit else: z: int = yield 44 yield x + y + z # Final value # This would be run in an async context # result = await fn_option() # assert result is Nothing ``` -------------------------------- ### Nested AsyncOption functions Source: https://github.com/dbrattli/expression/blob/main/README.md Example showing how AsyncOption functions can be nested and composed. The inner function is called from the outer function, and the result is awaited. ```python @effect.async_option[int]() async def inner_option(x: int) -> AsyncGenerator[int, int]: y: int = yield x + 1 yield y + 1 # Final value is y + 1 @effect.async_option[int]() async def outer_option() -> AsyncGenerator[int, int]: x: int = yield 40 # Call inner and await its result inner_result = await inner_option(x) y: int = yield await inner_result yield y # Final value is y # This would be run in an async context # result = await outer_option() # assert result == Some(42) # 40 -> 41 -> 42 ``` -------------------------------- ### Pipelining with object pipe method Source: https://github.com/dbrattli/expression/blob/main/README.md Demonstrates using the pipe method on Expression objects (e.g., Option) to chain operations. The example uses Some and maps functions over the value, asserting the piped result equals the nested application. ```python from expression import Option, Some v = Some(1) fn2: Callable[[Option[int]], Option[int]] = lambda x: x.map(lambda y: y + 1) gn2: Callable[[Option[int]], Option[int]] = lambda x: x.map(lambda y: y * 2) assert v.pipe(fn2, gn2) == gn2(fn2(v)) ``` -------------------------------- ### Composition with compose function Source: https://github.com/dbrattli/expression/blob/main/README.md Demonstrates composing functions directly into custom operators using the compose function. The example composes map, filter, and fold into a single callable and applies it to a sequence, asserting the result. ```python from expression import compose from expression.collections import Seq, seq mapper: Callable[[int], int] = lambda x: x * 10 predicate: Callable[[int], bool] = lambda x: x > 100 folder: Callable[[int, int], int] = lambda s, x: s + x xs = Seq.of(9, 10, 11) custom = compose( seq.map(mapper), seq.filter(predicate), seq.fold(folder, 0), ) ys = custom(xs) assert ys == 110 ``` -------------------------------- ### Pipelining with pipe function Source: https://github.com/dbrattli/expression/blob/main/README.md Shows how to use the pipe function to pass a value through multiple functions, similar to F#'s |> operator. The example defines two functions and asserts that piping is equivalent to nested function application. ```python from collections.abc import Callable from expression import pipe v = 1 fn1: Callable[[int], int] = lambda x: x + 1 gn1: Callable[[int], int] = lambda x: x * 2 assert pipe(v, fn1, gn1) == gn1(fn1(v)) ``` -------------------------------- ### AsyncResult with effect.async_result Source: https://github.com/dbrattli/expression/blob/main/README.md Example of using the AsyncResult type with the effect.async_result decorator for asynchronous error handling. It shows short-circuiting on error and how to yield values and await results. ```python from collections.abc import AsyncGenerator from expression import Error, Ok, effect @effect.async_result[int, str]() async def fn() -> AsyncGenerator[int, int]: x: int = yield 42 # Regular value y: int = yield await Ok(43) # Awaitable Ok value # Short-circuit if condition is met if x + y > 80: z: int = yield await Error("Value too large") # This will short-circuit else: z: int = yield 44 yield x + y + z # Final value # This would be run in an async context # result = await fn() # assert result == Error("Value too large") ``` -------------------------------- ### Sequence operations with pipe Source: https://github.com/dbrattli/expression/blob/main/README.md Shows how to use Expression's seq module with pipe to chain map, filter, and fold operations on an iterable, contrasting with nested functools calls. The example defines mapper, predicate, and folder lambdas and asserts the result equals the traditional reduce approach. ```python import functools from collections.abc import Iterable from expression import pipe from expression.collections import seq # Normal python way. Nested functions are hard to read since you need to # start reading from the end of the expression. xs: Iterable[int] xs = range(100) ys = functools.reduce(lambda s, x: s + x, filter(lambda x: x > 100, map(lambda x: x * 10, xs)), 0) mapper: Callable[[int], int] = lambda x: x * 10 predicate: Callable[[int], bool] = lambda x: x > 100 folder: Callable[[int, int], int] = lambda s, x: s + x # With Expression, you pipe the result, so it flows from one operator to the next: zs: int = pipe( xs, seq.map(mapper), seq.filter(predicate), seq.fold(folder, 0), ) assert ys == zs ``` -------------------------------- ### Result type with effect.result Source: https://github.com/dbrattli/expression/blob/main/README.md Example of using the Result type with the effect.result decorator to compose operations that may fail. The function yields Ok values and returns a Result instance. ```python from expression import Ok, Result, effect @effect.result[int, Exception]() def fn5() -> Generator[int, int, int]: x = yield from Ok(42) y = yield from Ok(10) return x + y xs = fn5() assert isinstance(xs, Result) ``` -------------------------------- ### Mixing fluent and functional syntax with pipe Source: https://github.com/dbrattli/expression/blob/main/README.md Shows that fluent and functional syntaxes can be mixed, using pipe fluently on a Seq. ```python from expression.collections import Seq, seq xs = Seq.of(1, 2, 3).pipe(seq.map(mapper)) ``` -------------------------------- ### Import Option and option module in Python Source: https://github.com/dbrattli/expression/blob/main/README.md Shows how to import and use the Option type and option module from the Expression library. The output demonstrates that Option is a class and option is a module, reflecting the Python naming convention where modules are lowercase and types are capitalized. ```python >>> from expression import Option, option >>> Option >>> option ``` -------------------------------- ### Initialize MyST notebook metadata with jupyter-book myst init Source: https://github.com/dbrattli/expression/blob/main/docs/markdown-notebooks.md Command to quickly add YAML metadata to a markdown file so that Jupyter Book treats it as a MyST Markdown Notebook. ```bash jupyter-book myst init path/to/markdownfile.md ``` -------------------------------- ### Combining Options with map2 Source: https://github.com/dbrattli/expression/blob/main/README.md Shows how to combine two Options using map2, which only applies the function if both are Some, otherwise returns Nothing. Includes assertions demonstrating the behavior. ```python from operator import add from expression import Nothing, Option, Some def keep_positive(a: int) -> Option[int]: if a > 0: return Some(a) else: return Nothing def add_options(a: Option[int], b: Option[int]): return a.map2(add, b) assert add_options(keep_positive(4), keep_positive(-2)) is Nothing assert add_options(keep_positive(3), keep_positive(2)) == Some(5) ``` -------------------------------- ### Import Matplotlib and set up interactive mode Source: https://github.com/dbrattli/expression/blob/main/docs/notebooks.ipynb Sets up Matplotlib for interactive plotting by importing necessary modules and enabling interactive mode with plt.ion(). ```python from matplotlib import rcParams, cycler import matplotlib.pyplot as plt import numpy as np plt.ion() ``` -------------------------------- ### Pattern matching on Option with match statement Source: https://github.com/dbrattli/expression/blob/main/README.md Shows how to check if an Option is Some using a match statement with the Option(tag='some') pattern. ```python from typing import Literal from expression import Ok, Option def exists(x: Option[int]) -> bool: match x: case Option(tag="some"): return True case _: return False ``` -------------------------------- ### Fluent syntax for sequence mapping/filtering/folding Source: https://github.com/dbrattli/expression/blob/main/README.md Uses the fluent API of Seq to map, filter, and fold a sequence. Recommended when using mypy for type checking due to better type inference through method chains. ```python from expression.collections import Seq xs = Seq.of(1, 2, 3) ys = xs.map(lambda x: x * 100).filter(lambda x: x > 100).fold(lambda s, x: s + x, 0) ``` -------------------------------- ### Effect-based option with yield from and short-circuiting Source: https://github.com/dbrattli/expression/blob/main/README.md Demonstrates an effect.option decorated generator that yields from Nothing to short-circuit the rest of the function, returning Nothing. The function never executes the remaining statements after yielding from Nothing. ```python from collections.abc import Generator from expression import Nothing, Some, effect @effect.option[int]() def fn4() -> Generator[int, int, int]: x = yield from Nothing # or a function returning Nothing # -- The rest of the function will never be executed -- y = yield from Some(43) return x + y xs = fn4() assert xs is Nothing ``` -------------------------------- ### Functional syntax with pipe for sequence operations Source: https://github.com/dbrattli/expression/blob/main/README.md Uses the pipe function with seq.map, seq.filter, and seq.fold for a more verbose but line-friendly style. Recommended for use with pylance/pyright. ```python from expression import pipe from expression.collections import Seq, seq mapper: Callable[[int], int] = lambda x: x * 100 xs = Seq.of(1, 2, 3) ys = pipe( xs, seq.map(mapper), seq.filter(lambda x: x > 100), seq.fold(lambda s, x: s + x, 0), ) ``` -------------------------------- ### Transform iterable with pipe and Seq Source: https://github.com/dbrattli/expression/blob/main/README.md Shows a practical pipeline using pipe and Seq to filter, map, and sum a list of scores. The result is asserted to be 54. ```python from expression import pipe from expression.collections import seq total = pipe( [4, 7, 12, 15], seq.filter(lambda score: score >= 10), seq.map(lambda score: score * 2), seq.sum, ) assert total == 54 ``` -------------------------------- ### Option type with Some and Nothing Source: https://github.com/dbrattli/expression/blob/main/README.md Defines a function that returns Some(a) for positive input and Nothing otherwise, demonstrating the Option type's two possible values. ```python from expression import Nothing, Option, Some def keep_positive(a: int) -> Option[int]: if a > 0: return Some(a) else: return Nothing ``` -------------------------------- ### Pattern matching a tagged union shape Source: https://github.com/dbrattli/expression/blob/main/README.md Demonstrates pattern matching on a Shape tagged union instance, checking the tag and rectangle case. The tag keyword argument is required for static type checkers to verify exhaustiveness, though not for runtime correctness. ```python shape = Shape.Rectangle(2.3, 3.3) match shape: case Shape(tag="rectangle", rectangle=Rectangle(width=2.3)): assert shape.rectangle.width == 2.3 case _: assert False ``` -------------------------------- ### Create a custom legend with Matplotlib Source: https://github.com/dbrattli/expression/blob/main/docs/notebooks.ipynb Generates a plot with a custom legend using Line2D objects to represent different data ranges. Fixes random state for reproducibility and uses a color cycler. ```python # Fixing random state for reproducibility np.random.seed(19680801) N = 10 data = [np.logspace(0, 1, 100) + np.random.randn(100) + ii for ii in range(N)] data = np.array(data).T cmap = plt.cm.coolwarm rcParams['axes.prop_cycle'] = cycler(color=cmap(np.linspace(0, 1, N))) from matplotlib.lines import Line2D custom_lines = [Line2D([0], [0], color=cmap(0.), lw=4), Line2D([0], [0], color=cmap(.5), lw=4), Line2D([0], [0], color=cmap(1.), lw=4)] fig, ax = plt.subplots(figsize=(10, 5)) lines = ax.plot(data) ax.legend(custom_lines, ['Cold', 'Medium', 'Hot']); ``` -------------------------------- ### Tagged Union definition with @tagged_union Source: https://github.com/dbrattli/expression/blob/main/README.md Defines a Shape tagged union with Rectangle and Circle cases using @tagged_union, @dataclass, and case()/tag() helpers. Includes optional static constructors and notes that the tag field is recommended for static type checkers. ```python from dataclasses import dataclass from typing import Literal from expression import case, tag, tagged_union @dataclass class Rectangle: width: float length: float @dataclass class Circle: radius: float @tagged_union class Shape: tag: Literal["rectangle", "circle"] = tag() rectangle: Rectangle = case() circle: Circle = case() @staticmethod def Rectangle(width: float, length: float) -> "Shape": """Optional static method for creating a tagged union case""" return Shape(rectangle=Rectangle(width, length)) @staticmethod def Circle(radius: float) -> "Shape": """Optional static method for creating a tagged union case""" return Shape(circle=Circle(radius)) ``` -------------------------------- ### MyST Markdown code cell with {code-cell} directive Source: https://github.com/dbrattli/expression/blob/main/docs/markdown-notebooks.md Defines a code cell in a MyST Markdown notebook using the {code-cell} directive. When the book is built, the cell is executed with the default Jupyter kernel and its output is displayed in-line. ```python print(2 + 2) ```