Hypo
タイトルの通りですがせっかく作ったので一応宣伝ということです.
はじめに
そもそも聞き慣れないと思いますがHyとはPythonのVM上で動くLisp方言です.たとえばPythonで書かれたイカしたLisp: Hyとかを読めば雰囲気がわかるかと思います.JVMに対するClojureのPython版と理解していただければよいです.というか見た目はまんまClojureなのでクソマイナー言語の割にマークダウンやReSTでそれらしくシンタクスハイライトできてしまいます.
Hyそのものは専用のインタプリタで実行できますが構文解析のオーバーヘッドがイマイチです.なので付属コマンドであるhycを使って.pycファイル(バイトコンパイルされたPythonスクリプト)に変換してから使うのがイイ感じです.また同様の付属コマンドであるhy2pyを使えば.pyファイルに変換することも一応可能ですが大抵そのままでは動かせません.なのでデバッグ用途ぐらいでしか使い物になりません.というのもHyが生成するPythonのASTには通常Pythonで用いることのできないシンボル名が頻繁に登場するためです.(例えばgensymで生成される変数名は:始まり)
以上のことはHyを使って日々のタスクを簡単にするような小さなアプリケーションを書きたいと言ったようなときに非常にイマイチです.いちいちhycするなりMakefileを書いたりしとけば済む話ですがあんまりクールじゃない.またPythonista的には.pycファイルを日々目にするのはなんとなく気持ち悪い.
じゃあHyのソースコードをまとめて実行形式にしちゃうようなのを作ってしまえというのでできたのがこの記事の本題です.
インストール
PyPIからどうぞ.
$ sudo pip install hypo
使い方
$ hypo -o 出力名 ソースファイル1 ソースファイル2..
のように使います.-oオプションが省略された場合はaという名前で出力されます.ちなみにアプリケーションのエントリーポイントはここでいうソースファイル1になります.なお入力は.hyファイルのみしか受け付けません.出力はzipappよろしく書庫ファイルですが予めシバンと実行権限を付与しているので実行バイナリのように考えていただいてかまいません.
使用例
例えば以下のような2つのファイルからなるアプリケーションをビルドしてみます.
(import [iota [iota]])
(defmain [&rest args]
(print (iota 10)))
(defn iota [m &optional [n 0] [step 1]]
(if (>= n m)
None
(cons n (iota m (+ n step) step))))
ちなみにこれらのコードはPythonでいうと以下のようなカンジです.
from iota import iota
if __name__ == '__main__':
print(iota(10))
def iota(m, n=0, step=1):
if n >= m:
return ()
return (n, ) + iota(m, n + step, step)
Pythonでは普通再帰は使いませんが関数型言語といえば再帰ということで.ただし末尾再帰では無いのでLisper的にもイマイチなコードであることは一応断っておきます.ちなみにHyで末尾再帰を使う場合はloop/recurマクロを使います.
で,本題に戻ります.ターミナルから以下のコマンドを実行します.なお,今回はexampleという名前でビルドします.
$ hypo -o example main.hy iota.hy
するとカレントディレクトリにexampleというファイルが作成されるので実行してみます.
$ ./example
(0L 1L 2L 3L 4L 5L 6L 7L 8L 9L)
main.hyの内容が実行されます.この時実行ファイルの内部では.pycにコンパイルされたファイルが呼び出されているのでHyのインタプリタから実行するよりちょっとだけ高速です.なお,数値の出力がキモいのはHyの仕様です.
まとめ
手前味噌な感じでHyのソースコードをまとめて実行形式にしちゃうようなのの使い方を紹介しました.あまり大規模なアプリケーションを作るのには向いていませんがぜひ使っていただけると嬉しいです.休日の暇つぶしに数時間で書き上げたパッケージですのでバグなり要望なりあればぜひプルリクなりissueなり投げてください.ここまで読んでいただきありがとうございました.