Tutorial: model domain alternatives

Tutorial: model domain alternatives#

Use a dataclass for one record shape. Use a tagged union when a value is exactly one of several alternatives with different data or behaviour.

from dataclasses import dataclass
from typing import Literal

from expression import case, tag, tagged_union


@dataclass(frozen=True)
class Address:
    city: str


@tagged_union
class Delivery:
    tag: Literal["pickup", "shipping"] = tag()
    pickup: str = case()
    shipping: Address = case()

    @staticmethod
    def Pickup(store: str) -> "Delivery":
        return Delivery(pickup=store)

    @staticmethod
    def Shipping(address: Address) -> "Delivery":
        return Delivery(shipping=address)

Match each case where behaviour differs#

Python structural pattern matching makes handling the alternatives direct and explicit.

def delivery_label(delivery: Delivery) -> str:
    match delivery:
        case Delivery(tag="pickup", pickup=store):
            return f"Collect from {store}"
        case Delivery(tag="shipping", shipping=Address(city=city)):
            return f"Ship to {city}"


assert delivery_label(Delivery.Pickup("Central")) == "Collect from Central"
assert delivery_label(Delivery.Shipping(Address("Oslo"))) == "Ship to Oslo"

Keep constructors descriptive and keep cases small. See the tagged-union reference for serialization and the full decorator API.