Show HN: Katharos 面向 Python 的函数式编程与 CSP 风格并发库
Show HN: Katharos Functional programming and CSP-style concurrency for Python

原始链接: https://github.com/kamalfarahani/katharos

**Katharos** 是一个将函数式编程抽象与强大并发能力相结合的 Python 库。通过利用函子(Functors)、单子(Monads,如 Maybe、Result、IO)以及代数结构等概念,Katharos 允许开发者将错误、状态和副作用处理为类型安全、可组合的值,而非传统的异常或 `None` 检查。 主要功能包括: * **单子控制流**:使用 `do-notation` 和链式调用来简化嵌套逻辑,消除可选值和错误传播所需的模板代码。 * **错误处理**:将错误视为 `Result` 类型。`Result.catch` 等特性可将高风险函数转换为安全、可追踪的值,无需手动编写 `try/except` 代码块。 * **函数式并发**:实现了 Go 风格的 CSP(通信顺序进程),其中通道操作返回 `Result` 对象,允许开发者将闭包和超时处理为数据。 * **结构化并发**:提供上下文管理器以确保所有并发任务均能完成,并支持可替换的后端,以实现灵活的性能扩展。 通过将函数式核心逻辑与消息传递并发相结合,Katharos 将复杂的错误管理和并发编排转化为简洁、易于预测且更易于维护的命令式风格代码。详细文档请访问 [katharos.readthedocs.io](https://katharos.readthedocs.io)。

```Hacker News最新 | 过往 | 评论 | 提问 | 展示 | 招聘 | 提交登录Show HN: Katharos 面向 Python 的函数式编程与 CSP 风格并发库 (github.com/kamalfarahani)19 个积分 由 kamalf 4 小时前发布 | 隐藏 | 过往 | 收藏 | 2 条评论 帮助 kamalf 4 小时前 [–] 我一直在开发 Katharos,这是一个面向 Python 3.13+ 的函数式编程和并发库。其核心理念很简单:缺失值、错误、副作用和并发通信应当是显式且可组合的值,而不是隐藏的控制流。回复rirze 39 分钟前 | 父评论 [–] 作为一个非常熟悉 Python 但不太了解“代数抽象”的人,我看了你的示例,却无法理解它们是如何工作的(以及为什么这种抽象是有用的)。是我错过了什么吗?回复 考虑申请 YC 2026 年秋季批次!申请开放至 7 月 27 日。 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:```
相关文章

原文

Katharos logo

A functional programming and concurrency library for Python. Katharos pairs algebraic abstractions (Functor, Applicative, Monad, Semigroup, Monoid) and concrete types like Maybe, Result, ImmutableList, and IO with message-passing concurrency built on the same functional core. The two halves share one idea: model errors, effects, and concurrent communication as composable, type-safe values. A concurrent hand-off returns a Result, so "the channel closed" is something you handle, not an exception you catch.

PyPI Docs License Coverage


Or using uv

Before: scattered None checks and exception handling:

user = find_user(user_id)
if user is None:
    return None
account = find_account(user)
if account is None:
    return None
return account.discount

After: do-notation that short-circuits cleanly on Nothing:

from katharos.types import Maybe
from katharos.syntax_sugar import do, DoBlock

@do(Maybe)
def lookup_discount(user_id: int) -> DoBlock[Maybe, float]:
    user    = yield find_user(user_id)
    account = yield find_account(user)
    return account.discount   # Just(0.15) or Nothing()

Before: nested try/except to propagate errors:

def process(raw: str) -> int:
    try:
        n = parse_int(raw)
    except ValueError as e:
        raise RuntimeError("bad input") from e
    try:
        return validate_positive(n)
    except ValueError as e:
        raise RuntimeError("bad value") from e

After: errors as values, chained with |:

from katharos.types import Result

def process(raw: str) -> Result[Exception, int]:
    return parse_int(raw) | validate_positive   # Failure short-circuits automatically

Handle optional values without None checks:

from katharos.types import Maybe

result = Maybe[int].Just(5) | (lambda x: Maybe[int].Just(x * 2))  # Just(10)
nothing = Maybe[int].Nothing() | (lambda x: Maybe[int].Just(x * 2))  # Nothing()

Model errors as values instead of exceptions:

from katharos.types import Result

def parse_int(s: str) -> Result[ValueError, int]:
    try:
        return Result.Success(int(s))
    except ValueError as e:
        return Result.Failure(e)

parse_int("42").fmap(lambda n: n * 2) # Success(84)
parse_int("??").fmap(lambda n: n * 2)  # Failure(...)

Skip the boilerplate with Result.catch:

Result.catch turns a function that raises into one that returns a Result, with no manual try/except. Only the declared exception type becomes a Failure; the caught exception keeps its traceback, so you can still find the line that failed.

import traceback
from katharos.types import Result

@Result.catch(ValueError)
def parse_int(s: str) -> int:
    return int(s)

parse_int("42")    # Success(42)
parse_int("??")    # Failure(ValueError("invalid literal for int() with base 10: '??'"))

failure = parse_int("??")
if failure.is_failure():
    traceback.print_exception(failure.error)  # full traceback, pointing at the failing line

Combine values with the Semigroup operator:

from katharos.types import ImmutableList

ImmutableList([1, 2]) @ ImmutableList([3, 4])  # ImmutableList([1, 2, 3, 4])

do-notation works with any monad: Maybe, Result, IO, ImmutableList and your custom monads. Each yield unwraps the monadic value:

from katharos.syntax_sugar import do, DoBlock
from katharos.types import Result

def parse_positive(x: int) -> Result[ValueError, int]:
    return Result.Success(x) if x > 0 else Result.Failure(ValueError(f"{x} is not positive"))

# Clean, imperative-style monadic code
@do(Result)
def do_block() -> DoBlock[Result, int]:
    x: int = yield parse_positive(5)
    y: int = yield parse_positive(3)
    return x + y

print(do_block())  # Success(8)

Katharos provides message-passing concurrency that builds on the same functional core, with room for more than one concurrency model. The first model available is Go-style CSP: launch work concurrently with go (like Go's go f(x)), communicate over typed Channels, and (crucially) receive values as a Result, so a closed or timed-out channel is a value you pattern-match, not an exception you wrap in try:

from katharos.concurrency.csp import csp

ch = csp.Channel[int](capacity=1)

csp.go(ch.send, 42)     # run work concurrently, like Go's `go f(x)`

ch.recv()               # Success(42)

ch.close()
ch.recv()               # Failure(ChannelClosedError(...)): closure is a value, not a raise

Used as a context manager, go becomes a structured-concurrency scope that joins everything spawned inside it before the block exits:

from katharos.concurrency.csp import csp

with csp.go:                 # scope waits for all work launched inside
    csp.go(worker, 1)
    csp.go(worker, 2)
# both workers have finished here

Every concurrency model is bound to a swappable BaseThreadingBackend (standard threads by default), and the csp runtime supplies it automatically, so you can retarget work onto a different backend in one place. Additional models (such as an actor model) are planned, built on the same backend abstraction and the same Result-valued, composable style.

Full tutorials, how-to guides, API reference, and explanations of the mathematical foundations are at katharos.readthedocs.io.

MIT

联系我们 contact @ memedata.com