Hylangイントロダクション

## はじめに

本記事は前回の導入の続きになります

https://qiita.com/hylanger/items/e7120ae34575ab3d8f65

Lispの知識がある人は、私の記事よりも以下の記事を参照すると良いと思います。

https://qiita.com/masatoi0/items/8215353612bc32ad0717

まずHylangの基本的な演算子や構文はpythonを前提としているので、pythonについてある程度知っておく必要があります。

pythonのコードをHylangに置き換える際のサンプルが[公式](https://hylang.org/)で提供されています。
通常利用の範囲であれば、この対応表でどうにかなりますが、ただ羅列されているだけでわかりにくいので、解説を入れて紹介します。

## Hylangをpythonに変換

解説に入る前に、自分で書いたHylangのコードがどのようなpythonのコードになるか知りたい場合があります。このときに便利なのが`hy2py`コマンドです。
このコマンドは`hy`コマンドをインストールした際に一緒にインストールされるので、別途インストールする必要はありません。

例えば次のコードに対して `hy2py test1.hy` を実行すると

```clojure
(defn test1 []
  (setv x 10)
  (let [x1 20
        x2 30]))
```

以下のpythonのコードが出力されます

```py
def test1():
    x = 10
    _hy_let_x1_1 = 20
    _hy_let_x2_2 = 30
```

このコマンドを実行してpythonにおける等価なコードがどうなっているのか確認すればよいのですが、1点問題があります。
コードを変換するのみという指定ができず、必ずプログラムを実行してしまいます。
プログラムの実行を回避するためには、実処理をファイルにベタ書きせず関数を定義する、エントリポイントをコメントアウトしておくなどの対策が必要になります。

## サンプルの解説

ここから各サンプルの対応を見ていきます。

### 代入

代入は`setv`を利用します。

```clojure
(setv foobar (+ 2 2))
(setv [tim eric] ["jim" "derrick"])
(setv  alpha "a"  beta "b")
```

対応するpythonのコードは以下になります。

```py
foobar = 2 + 2
[tim, eric] = ['jim', 'derrick']
alpha = 'a'
beta = 'b'
```

代入とは別にバインドも可能です。
バインドは本来は局所的な一時変数を定義して値を代入するためのものですが、Hylangの場合は変数名をユニークにした変数への代入になります。

```clojure
(let [x 10
      y 11
      z 12]
  (print (+ x y z)))
```

対応するpythonコードは以下になります。

```py
_hy_let_x_1 = 10
_hy_let_y_2 = 11
_hy_let_z_3 = 12
print(_hy_let_x_1 + _hy_let_y_2 + _hy_let_z_3)
```

### 関数の呼び出し

関数の呼び出しは以下のように行います。
キーワード引数は`:key`のようにコロンをつけて記述します。

```clojure
(sorted "abcBC"
  :key (fn [x] (.lower x)))
```

対応するpythonコードは以下になります。

```py
sorted("abcBC",
    key = lambda x: x.lower())
```

pythonには配列や辞書形式データを展開させて関数の引数に指定する方法もありますが、これも実現できます。

```clojure
(sorted #*["abcBC"] #**{"key" (fn [x] (.lower x))})
```

対応するpythonコードは以下になります。

```py
sorted(*['abcBC'], **{'key': lambda x: x.lower()})
```

### 関数定義

関数定義は以下の様に行います。
デフォルト値は`[c "x"]`の様に定義し、可変引数は`#* d`で定義しています。
この他にキーワード引数の定義(`#** e`など)も可能ですが、そもそも引数の個数は明確に定義されるべきなので、あまり利用しないほうが良いです。

```clojure
(defn test [a b [c "x"] #* d]
  [a b c d])
```

対応するpythonコードは以下になります。

```py
def test(a, b, c="x", *d):
    return [a, b, c, d]
```

### with句

with句は以下の様に記述します。
取り立てて解説することは無いのですが、メソッドの呼び出しを行っている箇所があります。
メソッドの呼び出しは通常の関数とことなり、インスタンスを経由して呼び出すので `(.append buffer xxxx)`のようになっています。
これは通常の関数呼び出しのように`(buffer.append xxxx)`のように書くこともできます。

```clojure
(with [o (open "file.txt" "rt")]
  (setv buffer [])
  (while (< (len buffer) 10)
    (.append buffer (next o))))
```

対応するpythonコードは以下になります。

```py
with open('file.txt', 'rt') as o:
    buffer = []
    while len(buffer) < 10:
        buffer.append(next(o))
```

### 最後に

この他サンプルでは`lfor`とマクロ定義を紹介していますが、あまり解説することがないので飛ばします。
マクロに関してはかなり難しいのでLispの文法に慣れた後に手を出すことをおすすめします。

およそ5年ぶりぐらいにHylangを触ってみたのですが、細かい仕様が変わっていて、昔の記憶のままにプログラムを書いてみたところ全く動きませんでした。
特に今回解説した関数の引数定義や呼び出しまわりで`apply`が使えなくなったり`&rest`キーワードが`#*`マクロに変更になるなど、ガラッと変わっていました。

次回は`Hyrule`について解説していきたいと思います。`Hyrule`を導入すると、より高階関数っぽい記述が出来るようになります。
元々はHylang本体の方に入っていた機能群だったと思うのですが、いつのバージョンからか分離されたようです。

## 参考
- https://hylang.org/
- https://ja.wikibooks.org/wiki/Lisp/%E5%A7%8B%E3%82%81%E3%81%AE%E4%B8%80%E6%AD%A9/%E7%B5%8C%E9%A8%93%E8%80%85%E7%94%A8%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB