nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pythonでreturnsを使って鉄道指向プログラミング体験

はじめに

七尾百合子さん、お誕生日 88日目 おめでとうございます! nikkieです。

今年はこころきゅんきゅんしている私ですが、この記事はこころぴょんぴょんするTIL (Today(※最近) I learned)です

目次

鉄道指向プログラミング

Railway oriented programming🛤️

なんでもこころぴょんぴょんするらしいですね1
鉄道指向でこころがぴょんぴょんする解説 | 黒曜の吹き溜まり

こころぴょんぴょん記事で「詳しく知りたい場合」に案内された記事がこちら。

今回考える世界では関数の出力は1つのみ2なので、Result型を使います。
1つの返り値で成功も失敗も表せるわけですね。

1入力でResult型を返す関数をつなげて、鉄道のようにプログラミングするというのが今の理解です。
図はGitHub(翻訳記事)から引用3

なお一度失敗用の路線に入ってしまうと(本来であれば)成功パスには決して戻さないという点に注意してください。

  • 成功の入力 -> 関数の出力は成功も失敗もある
  • 失敗の入力 -> 関数の出力は失敗のみ

o3-pro deep research による returns でのサンプルコード

Pythonで鉄道指向プログラミングの例をあまり見たことがないので、o3-proに生成してもらいました。

Python経験者向けにPythonでrailway oriented programmingの実装例

o3-proは returns を知っていて、調査してサンプルコードを生成してくれました。
https://pypi.org/project/returns/

10,20,30のような1行からなるCSVファイルを処理します。

  • load_data()
    • 成功時はファイルの中身の文字列を返す("10,20,30"
    • 例えば存在しないファイルのときは失敗
    • @safeデコレータ、めっちゃ便利ですね!
  • parse_numbers_csv()
    • 成功時は文字列を数値のリストに変換して返す("10,20,30" -> [10, 20, 30]
    • 数値でない文字列があったときは失敗
  • filter_non_positive()
    • 成功時は自然数のリストを返す([10, 20, 30] -> [10, 20, 30]
    • 負の数があったときは失敗
  • compute_average()
    • 成功時は平均値を返す([10, 20, 30] -> 20.0
    • ゼロ除算となる(=リストが空)ときは失敗

正常系(成功)4

% cat numbers.csv 
10,20,30
% pipx run rop_example.py numbers.csv 
SUCCESS: Average = 20.00

異常系(失敗)

存在しないファイルの指定

% pipx run rop_example.py not_exists.csv
FAILURE: FileNotFoundError(2, 'No such file or directory')

数値にできない文字列混入

% cat string.csv 
10,hoge,30
% pipx run rop_example.py string.csv    
FAILURE: ValueError("invalid literal for int() with base 10: 'hoge'")

負の数混入
(ここだけFailureの型が例外の代わりに文字列で実装されてます)

% cat minus.csv 
10,20,-30
% pipx run rop_example.py minus.csv     
FAILURE: 'Input contains non-positive numbers, which are not allowed.'

一度失敗すると、成功に戻ることはないというのをreturnsがうまくやってくれていますよね。
bindした一連の処理は最後まで流れたうえでどこで失敗したかが分かります

o3-proが見つけてきたreturnsのドキュメント中の鉄道指向のページ(積ん読):
Railway oriented programming - returns 0.25.0 documentation

終わりに

Pythonで鉄道指向プログラミングを体験しました。
returnsというライブラリはPythonでResult型ができると知っていましたが、鉄道を組んでみて便利さを体感しました。
失敗するかもしれない処理をひと繋ぎにbindできて、最終結果からどこで失敗したかが分かるというのはとても便利だと思います(こころぴょんぴょん!)。
過去にreturnsはResult型として試してみて「例外送出と何が違うんだろう?」という感想でしたが、鉄道指向でコードを書くとResult型がどれだけ便利かが分かりました。

P.S. 久々に見たごちうさ1話はコメントがめちゃ濃厚になってて面白かったです


  1. 『なっとく!関数型プログラミング』で純粋関数は戻り値は1つだけと学びました。そういえばPythonでは関数が複数の値(1つのタプル)を返せるな...
  2. https://github.com/yukitos/notes/blob/447aa3ae78a795e8df31484e2e26748c5cd93203/A_recipe_for_a_functional_app/img/02-09.png
  3. サンプルスクリプトは、PEP 723をサポートしているツール(pipxだけでなくuvやhatchでも)で動かせます