PythonistaのためのLisp入門：補足

# はじめに
[PythonistaのためのLisp入門（Hyチュートリアル和訳）](http://qiita.com/koji-kojiro/items/94c3bd97e1361557f7b9)の補足的な記事です．[Hy Tutorial](http://docs.hylang.org/en/master/tutorial.html)では**「Pythonでこう書くものはHyではこう書く」**的な内容がほとんどでしたがそれも実用には十分でないところが有ります．この記事ではHyならではの，言い換えると**PythonにはないHyの機能**および**Hy独自の特徴**について補足的に解説したいと思います．これを読めばとりあえずPythonの代わりにHyを使うには十分だと思います．

### あわせて読みたい
- [PythonistaのためのLisp入門（Hyチュートリアル和訳）](http://qiita.com/koji-kojiro/items/94c3bd97e1361557f7b9)
- [Pythonで書かれたイカしたLisp: Hy](http://qiita.com/t-sin/items/07ac1c7e413b43f8b6a8)
- [HyでDjangoアプリを実装する](http://qiita.com/csakatoku/items/77a36888d0851f6d4ff0)
- [Hyをネイティブコンパイルするツールを作った](http://qiita.com/koji-kojiro/items/91709f8340f8f8658080)

Qiita以外だと以下の良記事を見つけたので併せてご参照ください．（[前の投稿](http://qiita.com/koji-kojiro/items/94c3bd97e1361557f7b9)とモロかぶりのうえこちらのほうが有用です...）

- [ゼロから始めるHy(hylang) ](https://masatoi.github.io/2017/05/11/hy-tutorial#%E5%88%B6%E5%BE%A1%E6%A7%8B%E9%80%A0)

# 型
Pythonと同様の型をサポートしています．ただし，Python2.x系と3.x系で異なる部分が有ります．挙動は同じですが型が異なります．

## 整数型
Python2.xでは`long`，3.x系では`int`となります．

```clj:Python2での整数
=> (type 2)
<type 'long'>
```

```clj:Python3での整数
=> (type 2)
<class 'int'>
```

## 文字列型
Python2.xでは`unicode`，3.x系では`str`となります．

```clj:Python2での文字列
=> (type "a")
<type 'unicode'>
```

```clj:Python3での文字列
=> (type "a")
<class 'str'>
```

ちなみに文字列はダブルクォーテーション（`"`）で括ります．クォーテーション（`'`）はダメです．また，Pythonと異なり，途中での改行が認められています．

## nil
HyはLispなのでnilがないと始まらないように思います．しかしHyはPythonでもあるのでどちらかというと型に関してはPython準拠です．従って**nilはありません**．
文脈に応じて空のリスト(`[]` or `()`)，空のタプル（`(,)`），`False`，`None`などを用いましょう．LisperでないPythonistaな方は今までどおりで大丈夫です．

# 括弧の種類
Clojureと同様，他のLispたちとは異なり**括弧に区別が有ります**．

## (...)
リストを表します．ただし，Pythonの`list`とは微妙に異なる概念ですので区別のために*Lispのリスト*と呼ぶことにします．
*Lispのリスト*では最初の要素を関数とし，残りの要素をその引数として適用します．
従って`(function value1 value2 ...)`はPythonでいう`function(*[value1, value2 ...])`に相当します．

```clj:Lispのリスト
=> (+ 1 2 3)
6
=> (1 2 3)
Traceback (most recent call last):
  File "/usr/local/lib/python3.5/dist-packages/hy/cmdline.py", line 102, in runsource
    ast_callback)
  File "/usr/local/lib/python3.5/dist-packages/hy/importer.py", line 176, in hy_eval
    return eval(ast_compile(expr, "<eval>", "eval"), namespace)
  File "<eval>", line 1, in <module>
TypeError: 'int' object is not callable
```
二番目の例ではPythonでいう`1(*[2, 3])`を評価したこととなり，もちろん`1`は`callable`では無いのでエラーとなります．
関数としての評価をしたくない時にはクォーテーション(`'`)を用います．

```clj:Lispのリストwithクォーテーション
=> '(1 2 3)
(1 2 3)
```

## [...]
Pythonにおけるいわゆるリストを表します．またClojureに習ってベクタとも呼びます．これは先程のクォートした*Lispのリスト*と等価です．

```clj:Pythonのリスト
=> [1 2 3]
[1, 2, 3]
=> (= [1 2 3] '(1 2 3))
True
```

## {...}
Pythonと同じく辞書(`dict`)をあわらします．キーと要素を交互に並べます．

```clj:dict
=> {"a" 1 "b" 2}
{'b': 2, 'a': 1}
```
またキーは`:key`の形でも良いです．

```clj:dictその2
=> {:a 1 :b 2}
{'\ufdd0:a': 1, '\ufdd0:b': 2}
```

要素のアクセスは`get`を用います．

```clj:dictの要素にアクセス
=> (get {"a" 1 "b" 2} "a")
1
=> (get {:a 1 :b 2} :a)
1
```
また，その２の書き方(`:key`)の場合は以下のアクセスも可能です．

```clj:dictの要素にアクセスその２
=> (get {:a 1 :b 2} :a)
1
=> (:a {:a 1 :b 2})
1
```

### まとめ
ここまでを表にまとめます．また説明していませんでしたがタプルも表に含めています．

| Hyでの表現 | 対応するPythonでの表現 |
|:----------:|:-----------:|
| リスト <br> `(function arg1 arg2 ...)` | 関数の呼び出し <br> `function(*[args1, arg2, ...])`|
| クォートしたリスト <br> `'(elem1 elem2 ...)` | リスト <br> `[elem1, elem2, ...]` |
| ベクタ <br> `[elem1 elem2 ...]` | リスト <br> `[elem1, elem2, ...]` |
| タプル <br> `(, elem1 elem2 ...)` | タプル <br> `(elem1, elem2, ..., )` |
| 辞書その１ <br> `{"key1" elem1 "key2" elem2 ...}` | 辞書 <br> `{"key1": elem1, "key2": elem2, ...}` | 
| 辞書その2 <br> `{:key1 elem1 :key2 elem2 ...}` | 辞書 <br> `{"key1": elem1, "key2": elem2, ...}` | 



# 関数のオーバーロード（多重定義）
PythonではC++やJavaのように引数の型や個数に拠って関数の本体を切り替えることはできません．Hyでは`hy.contrib`以下のモジュールにこれを実現するマクロが用意されています．

## hy.contrib.multi.defn
組み込みのマクロである`defn`を拡張して引数の個数による多態を実現します．

```clj:defnによる個数オーバーロード
(require [hy.contrib.multi [defn]])

(defn function
  ([a] (+ "a = " (str a)))
  ([a b] (+ "a = " (str a) ", b = " (str b))))

(print (function 3))
;; > a = 3
(print (function 3 4))
;; a = 3, b = 4
```
上記の例では引数の個数がひとつの場合とふたつの場合で処理を切り替えています．なお，通常の`defn`と同じように使えばそのまま問題なく使えるので安心してください．

## hy.contrib.multi.defmulti, defmethod, default-method
多重ディスパッチによる特殊化を実現します．いわゆるマルチメソッドと呼ばれるものです．

```clj:defmultiによるマルチメソッド
(require [hy.contrib.multi [defmulti defmethod default-method]])

(defmulti add [x y] (, (type x) (type y)))

(defmethod add (, int str) [x y]
  (+ (str x) y))

(defmethod add (, str int) [x y]
  (+ x (str y)))

(default-method add [x y]
  (+ x y))

(print (add 1 "st"))
;; > 1st
(print (add "FF" 14))
;; > FF14
(print (add 2 4))
;; > 6
(print (add "Hello, " "world!"))
;; > "Hello, world!"
```

`defmulti`マクロでは引数の条件に用いる要素を定義します．今回の場合だと，２つの引数をとってその引数の型を格納したタプル`(, (type x) (type y))`をトリガーとしてディスパッチしています．`defmethod`マクロでは条件を設定し，条件ごとの実行内容を定義します．また`default-method`マクロはどの条件にも当てはまらなかった場合の実行内容を定義できます．上記の例では型をトリガーにしていますが，ぶっちゃけ何でもトリガーにできます．
例えば以下のようなコードも大丈夫です．

```clj:defmultiによるマルチメソッドその２
(require [hy.contrib.multi [defmulti defmethod default-method]])

(defmulti funtion [&rest args] (first args))

(defmethod funtion 1 [&rest args]
  (print "the list of arguments starts with 1"))

(defmethod funtion 2 [&rest args]
  (print "the list of arguments starts with 2"))

(default-method funtion [&rest args]
  (print "the list of arguments starts with something other than 1 and 2"))

(funtion 1 3 4)
;; > the list of arguments starts with 1
(funtion 2 3)
;; > the list of arguments starts with 2
(funtion 4 8 9 0)
;; > the list of arguments starts with something other than 1 and 2
```
上記の例では複数個の引数を受け取ってその先頭要素が何かでディスパッチしています．

# 遅延シーケンス
Pythonのジェネレータのようなものですが，ジェネレータとは異なり同じ要素に何度でもアクセスできます．もちろん，ジェネレータ同様遅延評価されます．要は，遅延評価＋メモ化が備わったリストのようなものです．`hy.contrib.sequences.defseq`マクロで定義できます．例えばフィボナッチが以下のように書けます．

```clj:遅延シーケンスでFibonacci
(require [hy.contrib.sequences [defseq]])
(import [hy.contrib.sequences [Sequence]])
;; マクロ展開後に使用されているのでimportしておかいといけない

(defseq fibonacci [n]
  (if (<= n 1) n
      (+ (get fibonacci (- n 1)) (get fibonacci (- n 2)))))

(print (get fibonacci 100))
;; > 354224848179261915075
```
上記のコードは遅延評価のおかげでミリ秒で終了しますが，普通に再帰関数で書くと恐ろしく時間がかかります．

# 先行ドット構文（2017/06/06追記）
Hyではメソッド呼び出しに*先行ドット構文*（leading dot syntax）というものが使えます．`(object.method args...)`を`(.method object args...)`のように書けるシンタックスシュガーです．

```clj:先行ドット構文（メソッド呼び出し）
=> (def a [1 2 3])
=> (.append a 4)
=> a
[1, 2, 3, 4]
```
```clj:先行ドット構文（モジュール名前空間へのアクセス）
=> (import ast)
=> (.parse ast "print(\"Hello, world!\")" :mode "eval")
<_ast.Expression object at 0xb6a2daec>
```

# スレッドマクロ（2017/06/06追記）
[チュートリアル](http://qiita.com/koji-kojiro/items/94c3bd97e1361557f7b9)の最後にも出てきましたが，Hyにはスレッドマクロという可読性を向上させる非常に便利な機能があります．これはClojureから引き継いだものです．いくつか種類が有りますので個別に説明します．

## ->, ->>
`(func3 (func2 (func1)))`を`(-> func1 func2 func3)`のように書けます．`->`の場合次の式の最初の引数としてチェーンされ，`->>`の場合最後の引数としてチェーンされます．具体例を示します．

```clj:->と->>の例
=> (def a 8.)
=> (-> a (/ 4) (- 1)) ;; (- (/ a 4) 1)
1.0
=> (->> a (/ 4) (- 1)) ;; (- 1 (/ 4 a))
0.5
```
## as->
`->`および`->>`では最初か最後にしか引数を渡せません．途中に渡したい場合や，関数によって渡す位置が変わる場合に対応できません．そこで活躍するのが`as->`です．

```clj:as->の例
=> (as-> a it
...      (/ it 4)
...      (- 1 it))
-1.0
```
ここでは`a`に`it`という一時的な名前をつけています．

## doto
すこし毛色が異なりますがまとめて紹介します．D言語のWith構文のようなもので，単一のオブジェクトに対するメソッド呼び出しの連続を簡単にするものです．`(obj.method1) (obj.method2) ...`を`(doto obj .method1 .method2 ...)`のように書けます．

```clj:dotoの例
=> (doto [2 3] (.append 1) .sort)
[1, 2, 3]
```

## スレッドマクロの使いどころ
括弧のネストが深くなる場合に特に有効です．Pythonではイマイチになってしまう表現をスマートに書ける場合も多く有ります．`"tanaka taro"`を`"Taro Tanaka"`に変換する例を考えてみましょう．

```py:Pythonで田中太郎
" ".join(map(str.capitalize, reversed("tanaka taro".split())))
```
処理の流れを追うのが少し面倒です．これと同様のことをHyでは以下のようにできます．

```clj:Hyで田中太郎
(->> "tanaka taro" .split reversed (map str.capitalize) (.join " "))
```
非常にすっきりした見た目であるだけでなく，処理の流れも左から右へスラスラ読めるようになりました．
要するにスレッドマクロとはRubyのメソッドチェーンやD言語のUFCSのようなものです．可読性が格段に向上しますので積極的に使いましょう．

# おわりに
その他思いつけば追記すると思います．また間違い等あればぜひご指摘ください．