trcks 🚂🐍
trcks is a Python library that allows
railway-oriented programming (ROP)
in two different type-safe programming styles.
Setup
trcks is available on PyPI.
Use your favorite package manager (e.g. pip, poetry, or uv) to install it.
Check the documentation for
detailed setup instructions,
including how to install the trcks skill for AI coding agents.
Usage
The following subsections demonstrate
both styles of railway-oriented programming (ROP)
supported by trcks.
Object-oriented style
The object-oriented style is based on method chaining,
as demonstrated by the get_subscription_fee_by_email function
in the following example.
>>> from typing import Literal
>>> from trcks import Result
>>> from trcks.oop import Wrapper
>>>
>>> UserDoesNotExist = Literal["User does not exist"]
>>> UserDoesNotHaveASubscription = Literal["User does not have a subscription"]
>>> FailureDescription = UserDoesNotExist | UserDoesNotHaveASubscription
>>>
>>> def get_user_id(user_email: str) -> Result[UserDoesNotExist, int]:
... if user_email == "erika.mustermann@domain.org":
... return "success", 1
... if user_email == "john_doe@provider.com":
... return "success", 2
... return "failure", "User does not exist"
>>>
>>> def get_subscription_id(user_id: int) -> Result[UserDoesNotHaveASubscription, int]:
... if user_id == 1:
... return "success", 42
... return "failure", "User does not have a subscription"
>>>
>>> def get_subscription_fee(subscription_id: int) -> float:
... return subscription_id * 0.1
>>>
>>> def get_subscription_fee_by_email(user_email: str) -> Result[FailureDescription, float]:
... return (
... Wrapper(core=user_email)
... .map_to_result(get_user_id)
... .map_success_to_result(get_subscription_id)
... .map_success(get_subscription_fee)
... .core
... )
>>>
>>> get_subscription_fee_by_email("erika.mustermann@domain.org")
('success', 4.2)
>>> get_subscription_fee_by_email("john_doe@provider.com")
('failure', 'User does not have a subscription')
>>> get_subscription_fee_by_email("jane_doe@provider.com")
('failure', 'User does not exist')
Notes w.r.t. object-oriented style
- The generic type
trcks.Resultallows domain errors to become part of a function's return type (subject to static type checking). - The class
trcks.oop.Wrapperprovides a convenient way to chaintrcks.Result-returning functions and "regular" functions (in a type-safe way). - The
map*andtap*methods oftrcks.oop.Wrapper(and the other wrapper classes) accept extra positional and keyword arguments, which are forwarded to the given function. - See the OOP and FP equivalence table for a side-by-side comparison with the functional style, and the glossary for definitions of key terms.
Functional style
The functional style is based on function composition,
as demonstrated by the get_subscription_fee_by_email function
in the following example.
>>> from typing import Literal
>>> from trcks import Result
>>> from trcks.fp.composition import Pipeline3, pipe
>>> from trcks.fp.monads import result as r
>>>
>>> UserDoesNotExist = Literal["User does not exist"]
>>> UserDoesNotHaveASubscription = Literal["User does not have a subscription"]
>>> FailureDescription = UserDoesNotExist | UserDoesNotHaveASubscription
>>>
>>> def get_user_id(user_email: str) -> Result[UserDoesNotExist, int]:
... if user_email == "erika.mustermann@domain.org":
... return "success", 1
... if user_email == "john_doe@provider.com":
... return "success", 2
... return "failure", "User does not exist"
>>>
>>> def get_subscription_id(user_id: int) -> Result[UserDoesNotHaveASubscription, int]:
... if user_id == 1:
... return "success", 42
... return "failure", "User does not have a subscription"
>>>
>>> def get_subscription_fee(subscription_id: int) -> float:
... return subscription_id * 0.1
>>>
>>> def get_subscription_fee_by_email(user_email: str) -> Result[FailureDescription, float]:
... # Explicitly assigning a type to `pipeline` might
... # help your static type checker understand that
... # `pipeline` is a valid argument for `pipe`:
... pipeline: Pipeline3[
... str,
... Result[UserDoesNotExist, int],
... Result[FailureDescription, int],
... Result[FailureDescription, float],
... ] = (
... user_email,
... get_user_id,
... r.map_success_to_result(get_subscription_id),
... r.map_success(get_subscription_fee),
... )
... return pipe(pipeline)
>>>
>>> get_subscription_fee_by_email("erika.mustermann@domain.org")
('success', 4.2)
>>> get_subscription_fee_by_email("john_doe@provider.com")
('failure', 'User does not have a subscription')
>>> get_subscription_fee_by_email("jane_doe@provider.com")
('failure', 'User does not exist')
Notes w.r.t. functional style
- The generic type
trcks.Resultallows domain errors to become part of a function's return type (subject to static type checking). - The modules
trcks.fp.compositionandtrcks.fp.monads.resultprovide a convenient way to chaintrcks.Result-returning functions and "regular" functions (in a type-safe way). - The
map*andtap*functions in the modules undertrcks.fp.monadsaccept extra positional and keyword arguments, which are forwarded to the given function. - See the OOP and FP equivalence table for a side-by-side comparison with the object-oriented style, and the glossary for definitions of key terms.
Still not convinced?
Explore these repositories for more ways to use trcks:
- trcks-example-cyclopts (CLI application)
- trcks-example-fastapi (REST backend application)
Or check the motivation
for railway-oriented programming
and
for trcks.
Metadata
Release files for trcks 0.7.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| trcks-0.7.3.tar.gz | 84.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| trcks-0.7.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 185.2 kB
Release files / trcks-0.7.3.tar.gz
| Download URL | trcks-0.7.3.tar.gz |
|---|---|
| Size | 84.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3d2f01b8856e618bf95359b46e3800af216fb3b2e7e89334cc5c1995f1883b23
|
|
BLAKE2b-256 checksum How to use checksums |
3050a6e389c6fcfe81f09f72237a74e9604e7713ee59a7355e429425b5390349
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / trcks-0.7.3-py3-none-any.whl
| Download URL | trcks-0.7.3-py3-none-any.whl |
|---|---|
| Size | 100.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fc43358b63445be60d6fb41f369e4fe50db7a4f6084d5b036fb02f057a265aa8
|
|
BLAKE2b-256 checksum How to use checksums |
54a14c734eb449ad1bb407b4f5618cfc696ec024807f80b1f71c67070a1996b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|