Skip to main content

Type-safe railway-oriented programming (ROP)

Project description

trcks 🚂

trcks is a Python library that allows railway-oriented programming (ROP) in two different type-safe programming styles.

Railway-oriented programming (ROP) styles

Object-oriented style

The object-oriented style is based on method chaining. In the following example,

  • the generic type trcks.Result allows domain errors to become part of a function's return type (subject to static type checking) while
  • the class trcks.oop.Wrapper provides a convenient way to chain trcks.Result-returning functions and "regular" functions (in a type-safe way).
>>> from typing import Literal, Union
>>> from trcks import Result
>>> from trcks.oop import Wrapper
>>>
>>> UserDoesNotExist = Literal["User does not exist"]
>>> UserDoesNotHaveASubscription = Literal["User does not have a subscription"]
>>> FailureDescription = Union[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')

Functional style

The functional style is based on function composition. In the following example,

  • the generic type trcks.Result allows domain errors to become part of a function's return type (subject to static type checking) while
  • the modules trcks.fp.composition and trcks.fp.monads.result provide a convenient way to chain trcks.Result-returning functions and "regular" functions (in a type-safe way).
>>> from typing import Literal, Union
>>> 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 = Union[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')

Setup

trcks is available on PyPI. Use your favorite package manager (e.g. pip, poetry or uv) to install it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trcks-0.3.4.tar.gz (23.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trcks-0.3.4-py3-none-any.whl (24.4 kB view details)

Uploaded Python 3

File details

Details for the file trcks-0.3.4.tar.gz.

File metadata

  • Download URL: trcks-0.3.4.tar.gz
  • Upload date:
  • Size: 23.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.8.6

File hashes

Hashes for trcks-0.3.4.tar.gz
Algorithm Hash digest
SHA256 d39873cf9be4b1acfbd5102eeac87738c7d6f8195459128f47c5c93ab4cbc82c
MD5 2c479ca1c7aaaed8e59ee6d47e840df5
BLAKE2b-256 aafb9771a916782bec3b867d936dee52bf0d0f9536737acb90914c3a54f10bfc

See more details on using hashes here.

File details

Details for the file trcks-0.3.4-py3-none-any.whl.

File metadata

  • Download URL: trcks-0.3.4-py3-none-any.whl
  • Upload date:
  • Size: 24.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.8.6

File hashes

Hashes for trcks-0.3.4-py3-none-any.whl
Algorithm Hash digest
SHA256 f7f96b71989b91b3f7ea214b2a49ffaf53eee58fc11d1efad760cdcc75cc5231
MD5 eb2fbfd384dde0c9cb2eaeafa6793d6c
BLAKE2b-256 c535ddb932fb71548e5d7bd644aceba477316c558da18e5bcf5edfe435b44aa5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page