Skip to main content

match-expression

A Python implementation of TypeScript's ts-pattern, bringing powerful, type-safe pattern matching to Python with an expressive, chainable API.

Features

  • Chainable API: Intuitive match(value).case(pattern, then).exhaustive() syntax
  • Type-safe: Full type inference support with pyright/mypy
  • Exhaustiveness checking: Ensures all cases are handled at compile time
  • Zero dependencies: Lightweight and fast
  • Pythonic: Leverages Python 3.12+ type system features

Installation

pip install match_expression

Quick Start

from typing import Literal
from match_expression import match

# Literal type matching
def process_status(status: Literal["pending", "success", "error"]) -> int:
    return (
        match(status)
        .case("pending", 0)
        .case("success", 1)
        .case("error", -1)
        .exhaustive()
    )

# Type matching with classes
class Dog:
    def bark(self) -> str:
        return "Woof!"

class Cat:
    def meow(self) -> str:
        return "Meow!"

def handle_animal(animal: Dog | Cat) -> str:
    return (
        match(animal)
        .case(Dog, lambda d: d.bark())
        .case(Cat, lambda c: c.meow())
        .exhaustive()
    )

Examples

Literal Type Matching

from typing import Literal
from match_expression import match

type Platform = Literal["web", "mobile", "desktop"]

def get_app_name(platform: Platform) -> str:
    return (
        match(platform)
        .case("web", "Web Application")
        .case("mobile", "Mobile App")
        .case("desktop", "Desktop Software")
        .exhaustive()
    )

# Type checker knows all cases are covered!

Class Type Matching

from match_expression import match

class Success:
    def __init__(self, value: str):
        self.value = value

class Error:
    def __init__(self, message: str):
        self.message = message

def handle_result(result: Success | Error) -> str:
    return (
        match(result)
        .case(Success, lambda s: f"Success: {s.value}")
        .case(Error, lambda e: f"Error: {e.message}")
        .exhaustive()
    )

Using otherwise for Default Cases

from match_expression import match

def classify_number(n: int) -> str:
    return (
        match(n)
        .case(0, "zero")
        .case(1, "one")
        .case(2, "two")
        .otherwise("many")
    )

Mixed Return Types

The library correctly infers union return types:

from match_expression import match

def process(value: int | str) -> int | str:
    return (
        match(value)
        .case(int, lambda i: i * 2)      # Returns int
        .case(str, lambda s: s.upper())  # Returns str
        .exhaustive()
    )
    # Type is inferred as: int | str

Delayed Evaluation with eval=False

You can defer the evaluation of callable functions by using eval=False:

from match_expression import match
from typing import Callable

def get_handler(command: str) -> Callable[[], str]:
    return (
        match(command)
        .case("start", lambda: "Starting application...")
        .case("stop", lambda: "Stopping application...")
        .case("restart", lambda: "Restarting application...")
        .exhaustive(eval=False)  # Returns the lambda without calling it
    )

# Get the handler function without executing it
handler = get_handler("start")
# Execute later when needed
result = handler()  # "Starting application..."

This is useful when you want to:

  • Return handler functions for later execution
  • Implement lazy evaluation patterns
  • Build command dispatch systems

API Reference

match(value: V) -> Match[V]

Starts a pattern matching chain.

.case(pattern: P, then: R) -> Case[V, P, R]

Matches against a pattern. If the pattern matches, executes then.

  • pattern: A value to match against (for literals) or a type (for isinstance checks)
  • then: The value to return or a function to execute with the matched value

.exhaustive(eval: bool = True) -> R

Ensures all cases are handled. Raises ExhaustiveError if not all cases are covered.

  • eval: When True (default), evaluates callable functions. When False, returns the callable without evaluating it.

.otherwise(default: R, eval: bool = True) -> R

Provides a default value for unmatched cases.

  • default: The value to return or a function to execute when no patterns match
  • eval: When True (default), evaluates callable functions. When False, returns the callable without evaluating it.

Type Checking

The library is designed to work with type checkers like pyright and mypy:

# Install pyright
pip install pyright

# Type check your code
pyright your_file.py

Contributing

Contributions are welcome! Here's how to get started:

  1. Clone the repository
git clone https://github.com/qodot/match-expression.git
cd match-expression
  1. Install development dependencies
uv sync --dev
  1. Run tests
uv run pytest
  1. Type check
uv run pyright src/ tests/

Requirements

  • Python 3.12 or higher
  • No external dependencies

Special Thanks

Release files for match-expression 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for match-expression 0.3.1
File Size Uploaded
match_expression-0.3.1.tar.gz 6.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for match-expression 0.3.1
File Interpreter ABI Platform
match_expression-0.3.1-py3-none-any.whl Python 3 none any Details

Total release size: 11.4 kB

Release files / match_expression-0.3.1.tar.gz

Download URL match_expression-0.3.1.tar.gz
Size 6.2 kB
Tags Source
SHA-256 checksum
How to use checksums
82b80b5de4063ac824fb431e47fcc3e59db9649658dd1c9aed82854d0e1d6769
BLAKE2b-256 checksum
How to use checksums
7e6eb3c0719daecfec905dac78054e76058d88a37d17517b88d6aa98659957ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.10

Release files / match_expression-0.3.1-py3-none-any.whl

Download URL match_expression-0.3.1-py3-none-any.whl
Size 5.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3487e4209a4a1530f0bed45fe261d3e6178d99d687c09caa9d510bd10ecca2db
BLAKE2b-256 checksum
How to use checksums
33ba4fe3c8d49634bda1f7403821ad7561f433b0ee8b3f0eab4dc6182bbc70c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.10

Release history Release notifications | RSS feed

This release

0.3.1 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page