Katharos: Pythonのための関数型プログラミングとCSPスタイルの並行処理

Katharosは、Python 3.13+向けの関数型プログラミングおよび並行処理ライブラリであり、例外やNoneチェックのような隠れた制御フローを、明示的で合成可能かつ型安全な値に置き換えます。代数的抽象化とメッセージパッシングによる並行処理モデルを組み合わせることで、Katharosは開発者がエラー、オプション性、および並行通信を第一級の値として扱えるようにします。

代数的抽象化と型安全な値

Katharosは、ボイラープレートを排除し、null値や例外に関連するランタイムエラーを削減するために、コアとなる関数型プログラミングの抽象化を実装しています。

MaybeによるNoneチェックの排除

散在するif x is Noneチェックの代わりに、KatharosはMaybe型を使用します。これにより、値が存在しない場合にロジックをクリーンにショートサーキット(短絡評価)させることができます。

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  # Returns Just(0.15) or Nothing()

Resultによる値としてのエラーハンドリング

Katharosは、エラーを例外ではなく値として扱います。Result型を使用すると、パイプ演算子(|)を使用してエラーをチェーン化できます。ここで、Failureは自動的にチェーンの残りの部分をショートサーキットさせます。

手動のtry/exceptボイラープレートを減らすために、@Result.catchデコレータは、特定の例外を発生させる関数を、デバッグ用の元のトレースバックを保持しつつ、Result型を返す関数に変換します。

from katharos.types import Result

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

# Returns Success(42) or Failure(ValueError(...))
result = parse_int("42")

追加の関数型タイプ

  • ImmutableList: Semigroup演算子(@)を使用して組み合わせることができる不変シーケンスを提供します。
  • Algebraic Core: このライブラリは、異なる型間で一貫した合成を保証するために、FunctorApplicativeMonadSemigroup、およびMonoidの形式的な実装を提供します。

CSPスタイルの並行処理

Katharosは、GoスタイルのCommunicating Sequential Processes (CSP) モデルを並行処理に実装しており、並行タスク間の通信が関数型データ型と同様に型安全であることを保証します。

メッセージパッシングとチャネル

タスクはcsp.goを使用して並行に実行され、通信は型付きのChannelを介して行われます。重要な点は、チャネルから値を受信することはResultを返すことです。つまり、閉じたチャネルやタイムアウトしたチャネルは、キャッチすべき例外ではなく、パターンマッチングすべき値となります。

from katharos.concurrency.csp import csp

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

# Run work concurrently
csp.go(ch.send, 42)

# Receive as a Result value
ch.recv()  # Success(42)

ch.close()
ch.recv()  # Failure(ChannelClosedError(...))

構造化された並行処理

コンテキストマネージャとして使用する場合、csp.goは構造化された並行処理のスコープを作成します。プログラムはそのスコープを抜ける前に、そのブロック内で生成されたすべてのタスクをjoin(結合)するため、goroutineのようなタスクのリークを防ぎます。

with csp.go:
    csp.go(worker, 1)
    csp.go(worker, 2)
# Both workers are guaranteed to have finished here

プラグイン可能なバックエンド

Katharosのすべての並行処理モデルは、BaseThreadingBackendにバインドされています。標準的なスレッドはデフォルトですが、この抽象化により、ランタイムを単一の場所で異なるバックエンドに切り替えることが可能です。

将来のアップデートでは、この同じバックエンド抽象化に基づいたアクターモデルの追加が計画されています。

実装の詳細とコミュニティのフィードバック

Do-Notation(Do構文)のメカニズム

Katharosは、Pythonのジェネレータを介してdo-notationを提供し、命令的なスタイルのモナド的コードを記述できるようにします。しかし、コミュニティのフィードバックでは、Pythonにcall/cc(call-with-current-continuation)が欠在けているため、特定のモナド(リストモナドなど)の実装はジェネレータを再実行することになり、その使用法がより単純なユースケースや「おもちゃ」の例に限定される可能性があることが指摘されています。

"It's probably worth being up front about what your do syntax actually does. A generator is not as general as "do", Python doesn't have call/cc or anything like that."

要件

Katharosは**Python 3.13+**向けに設計されています。

Sources