Hy 1.0 – Python向けのLisp方言
(github.com/hylang)- Pythonに組み込まれるLisp方言である Hy 1.0.0 が、約12年にわたる断続的な開発と実運用を経てリリースされた
- Hy 1.x では、文書化された言語要素の 互換性破壊 は major バージョンの増加につながり、CPython 開発者がもはやサポートしない Python バージョンのサポート終了は例外とされる
- 今後の焦点は、新たなバグ修正、新しい Python バージョンへの互換性追加、既存の Hy 1.x.y コードを壊さない形での Python 新機能のサポート に置かれる
- あるユーザーは、Hy マクロによって SQLAlchemy ORM の宣言を
defmodel形式に簡潔化する例を挙げ、マクロ展開であるため ランタイムの追加コストがない と説明している - MicroPython 上で直接動作するかについては、標準ライブラリの部分集合しかないため難しそうだが、CPython で
hy2pyに変換したあと MicroPython がサポートする構成だけを使う方法なら可能かもしれない
Hy 1.0.0 リリース
- Hy 1.0.0 がリリースされた
- Hy は Python に組み込まれる Lisp方言 である
- リリースまでほぼ12年にわたり断続的な開発と多くの実運用があった
- 紹介とドキュメントは Hylang.org で提供されている
- バージョン履歴は NEWS file で確認できる
- よりくだけたリリース関連の投稿として HYPE POST もあわせてリンクされている
1.x 互換性ポリシー
- 今後、文書化された言語部分に対する breaking change は major バージョン番号を引き上げる
- 例外は、CPython 開発者がすでにサポートしていない Python バージョンのサポートを打ち切る場合である
- major バージョンの増加が頻繁に発生しないようにする意図が示されている
- 今後の作業の焦点は次の通り
- 新たに発生する バグ修正
- 新しい Python バージョンとの互換性追加
- 以前の Hy 1.x.y で動作するコードを壊さない形での Python 新機能サポート
Hy マクロ活用例: SQLAlchemy モデル DSL
- あるユーザーは、Hy で ORM と非同期 Web フレームワークを作っており、どちらも既存ライブラリを活用し、その大半が マクロ で構成されていると説明している
- SQLAlchemy ORM Quick Start Guide のモデル宣言を、Hy の
defmodelマクロでより短く表現する例が示されているUser、Addressモデルと関係宣言を Hy の S式形式で記述するdefmodelは SQLAlchemy コードへ展開されるマクロなので、ランタイムの追加コスト はない
- ユーザーは、Hy の内部仕様と API が安定化したことがこうした作業に必要だと述べている
関係宣言とクエリ DSL の議論
defmodelの例は複数の関係パターンへ拡張されている- one-to-one:
1->1 - many-to-many:
*->*とdeflinktable - timestamp フィールド:
:timestamp-create,:timestamp-update
- one-to-one:
- 宣言後のオブジェクトは一般的な SQLAlchemy 2.0 ORM オブジェクト なので、データベーススキーマ DSL 部分だけを Hy で書き、それ以外は Python で書ける
- クエリ DSL はまだ作業中で、fluent interface は S式とあまり相性がよくないと言及されている
- あるコメントでは、SQL の syntactic order と semantic order が一致するようにすべきだと提案し、Google Research の SQL Has Problems. We Can Fix Them: Pipe Syntax In SQL が紹介されている
- これに対して、
|>のような形式や list comprehension をまねる方法はあり得るが、最終的な設計は小さなアプリを作ってリファクタリングする反復のあとに出てくるだろうと返答している
MicroPython の可能性とリリースへの反応
- CPython の代わりに MicroPython 上で動作できるかという質問が出た
- 回答では、MicroPython は Python 標準ライブラリのごく小さな部分集合しか含まないため、直接動作は難しそうだとされている
- ただし、CPython で
hy2pyを使って Python コードへ変換したあと、MicroPython がサポートする構成に限定すれば、その出力を実行できる可能性はある - リリース当日には、Hylang.org をホスティングしている Web サーバーの日次ユニーク訪問者数が大きく増加したことも確認されている
1件のコメント
Hacker News のコメント
Hy が実際に Python へどう変換されるのかを把握するのが少し難しく、コンパイルされるのかインタープリトされるのかも最初は確信が持てなかった。
結局 Wikipedia で「Hy は s-expression を Python の**抽象構文木(AST)**に変換し、Python と相互作用できるよう設計された Lisp 方言」という説明を見つけ、GitHub のコードを見るとコンパイラも Python で書かれているようだ: https://github.com/hylang/hy/blob/master/hy/compiler.py
メインサイトでも「Python に埋め込まれた Lisp 方言」より、「Python AST にコンパイルされる Lisp 方言」と紹介したほうがより明確だと思う。Lisp を埋め込む方法には、インタープリタを付ける、Python バイトコードへコンパイルする、Python ソースコードへ変換するなどいくつもあり、それぞれトレードオフがかなり違う
通常、Hy のソースコードは Python AST オブジェクトになり、Python がそれをコンパイルして実行する。ただし Python AST オブジェクトを Python ソーステキストに変えることも可能で、Hy から Python を使ったり Python から Hy を使ったりする相互運用も可能: https://hylang.org/hy/doc/v1.0.0/interop
最終的にはすべて Python バイトコードになるので、うまく動く
下の動画の 16:25 あたりで見られる: https://m.youtube.com/watch?v=1vui-LupKJI
Hy は数年かけて成熟する中で、より Lispy でより Pythony でないものにするために構文糖衣や特殊ケースを積み重ねるのではなく、むしろ Python の機能セットに対する薄い構文抽象化に近づいたように思う。
つまり、別の方法ではまねしにくい核心、特にマクロに集中する方向だ。最近の例では、
matchは単に Python ネイティブのmatchで、Python 3.10 より前のバージョン向けのポリフィルもない。以前はfoo?が特殊ケースとしてis_fooにマングリングされていたが削除され、hy.evalは Python のevalにより近い形へ作り直され、unlessのような、あれば便利だが中核ではないユーティリティは Hyrule パッケージへ移されることが多い。最初は直感に反する方向のように感じたが、Python から Hy に移るときの学習曲線を下げ、Python パッケージと一貫して相互運用しやすくなるという利点が大きい。もしかすると言語メンテナンスを単純化する意図もあるのかもしれないし、記憶では
letもまともに作るまでに4回ほど試みていた気がするHy コードを書くときも、Hy のドキュメントより Python やサードパーティの Python ライブラリのドキュメントを見ることのほうがはるかに多い。Python を改善できる点はいろいろあると感じていたが、以前のように Clojure に配慮して
Trueをtrueと書けるようにした機能は、単に不要な複雑さだったHYPE POST は本当に良かった。エンタープライズソフトウェアの仕事をしている立場から見ると、風刺のセンスがとても優れている。
https://github.com/hylang/hy/discussions/2609
ついに出たので、これで https://github.com/rcarmo/sushy を整理できそうだ。
数年かけて少しずつ触ってきたが、Hy をアップグレードするたびに構文の一部が壊れたり、機能が Hyrule パッケージに入ったり出たりして困っていた。
ちなみに Hy は iPad の https://holzschu.github.io/a-Shell_iOS 内でもかなりよく動く。ただし vim/neovim のシンタックスハイライトは 0.29+ リリースと非同期に追いつく必要がある。
これまで Fennel と Guile も使ってみたが、Python ライブラリとエコシステムにアクセスできるほうがよい。非同期のおかげで効率的な API 作業もうまくできるし、いくつかの Scheme ではソケット再利用やヘッダーを細かく制御しながら HTTPS を扱うのがいまだに面倒なので、aiohttp を使うほうがはるかに良いと思う
初期のささやかな出発点と比べると、本当に遠くまで来た。
以前 PyCon で Hy を初めて世に紹介したライトニングトークを見て、その少し後に Paul と会い、初期の Hy バージョンに貢献し始めた。Common Lisp 風の
kwargs、内部の一部、ライブラリを1つか2つ担当した。遊び心は有用で、特に情熱を保つ助けになる。ハッカーがハッカーらしく振る舞えるし、すべてがビジネスである必要はないという点がいい。もう何年も関わっていないが、プロジェクトが進み続けているのを見るとうれしいし、素晴らしい節目だと感じる
Clojure ファンなら、関連して Python 3.8+ を対象にした、Clojure 互換に近い Lisp 方言 Basilisp もある。
https://github.com/basilisp-lang/basilisp
Lispではありませんが、Pythonへトランスパイルされる関数型プログラミング言語という観点では Coconut も興味深いです: https://coconut-lang.org/
HyやCoconutを実際に使った人たちの体験談が本当に気になります
生成されたコードの先頭にハードコードされた補助関数が大量に付き、同じ内容を普通のPythonで書いた場合よりもプログラムがずっと遅くなりました
逆にHyは、
letのスコープ処理で生じるわずかな間接層や返り値まわりの違いを除けば、人間が手で書きそうなPythonコードにかなり近いコードを生成します。ただしCoconutもかなり時間が経っているので、改善されている可能性はありますおめでとうございます。2つ気になります。REPL駆動開発をサポートしているのか、たとえばコンディションシステムやbreakloopのようなものがあるのかが気になります
それと、スタンドアロン配布版があるのかも気になります。Pythonの配布自体が面倒なので、理想的にはすべての依存関係を含んだ単一のHyバイナリを1つ配布できるとよいです。静的リンクでも、一時ディレクトリに展開するzipファイルでも構いません
uvx hy@1.0.0でHy REPLに入れますecho '(print "hi hn")' > hi.hyのあとにuvx hy@1.0.0 hi.hyを実行すると、"hi hn"を出力しますhttps://docs.astral.sh/uv/guides/tools/#running-tools
ちなみにuvはPythonバージョンのインストールと管理もできます
スタンドアロン配布版はありません
コードを対話的にデバッグするには、デバッグしたいコードに
(.run (hy.REPL :locals {#\* (globals) #\* (locals)}))を入れることができ、Pythonではimport hy; hy.REPL(locals = {\*globals(), \*locals()}).run()のように書けますただし
code.interact()と同様に、REPL内でローカル変数を変更しても元のスコープには伝播しませんpy2appと慎重なハックで、2番目に近いことはやったことがありますすべてをバイトコードにコンパイルしたうえで、Pythonの単一ファイル配布ツールを使えます
しかし、コンディションシステムやhandlers/restartsのようなものはありません
あまり真面目でない部分がとても良かったです。もっと多くのプログラミングプロジェクトが奇抜さを受け入れてくれたらいいと思います
もしかすると、それこそがPythonの伝統を称える最良の方法なのかもしれません
ただ、ある時点でやりすぎたと思うようになり、最近は遊び心を少し戻そうとしています
Hyを断続的に使ってきて、transformerを作ってみようとしてHyで書いた https://github.com/kunalb/orphism も公開しました
そろそろまた取り出して、ちゃんと動かしてみる時期のようです