underscore.jsでやっていたことは、すべてClojureScriptがやってくれる

[id:ayato_p](http://ayato.hateblo.jp/about)さんの[Underscore.jsがちょっと便利だったので紹介してみる。](http://ayato.hateblo.jp/entry/20140216/1392543496)を読んで思ったのが、Underscore.jsって最近使っていないな、ということでした。

というのも、1年くらい前までは、Underscore + Backboneで素のJavascriptを書いていたのですが、最近は[ClojureScript](https://github.com/clojure/clojurescript)を書くようになったからです。

## はじめに

ClojureScriptではJavascriptのオブジェクトは直接は使いません。
JavascriptでコレクションというとArrayになりますが、ClojureScriptではSequence,Vector,ListがありJavascriptのArrayとは別物です。

リテラルで、`[1,2,3]`と書くとVectorを表すことになります。そして","は空白と等価なので`[1 2 3]`と表現出来ます。

さて、ClojureScript試してみるには、CoffeeScriptと同じくブラウザさえあればよいです。

http://clojurescript.net/

でREPLが使えます。

では、ayato_pさんのUnderscore.jsのexamplesが、ClojurScriptだとどうなるのか、それぞれ試してみましょう！

## Collection Functions

### each関数

eachそのものはClojureScriptにはありません。[doseq](http://clojuredocs.org/clojure_core/clojure.core/doseq)と近しいのですが、eachは引数のArrayがそのままreturnされます。なので、こう定義できます。

```clj
cljs.user=> (defn each [li f] (doseq [x li] (f x)) li)
#<function each(li,f){...}>

cljs.user=> (each [1 2 3] println)
1
2
3
[1 2 3]

```

defnは、関数定義です。`function each(lli, f) {...}`を表します。

### map関数

ClojureScriptにとって、[map](http://clojuredocs.org/clojure_core/clojure.core/map)は最頻出関数です。FizzBuzzはこんな感じになります。

```clj
cljs.user=> (map #(cond (= (mod % 15) 0) "FizzBuzz" (= (mod % 5) 0) "Buzz" (= (mod % 3) 0) "Fizz" :default n) (range 100))
("FizzBuzz" nil nil "Fizz" nil "Buzz" "Fizz" nil nil "Fizz" "Buzz" nil "Fizz" nil nil "FizzBuzz" nil nil "Fizz" nil "Buzz" "Fizz" nil nil "Fizz" "Buzz" nil "Fizz" nil nil "FizzBuzz" nil nil "Fizz" nil "Buzz" "Fizz" nil nil "Fizz" "Buzz" nil "Fizz" nil nil "FizzBuzz" nil nil "Fizz" nil "Buzz" "Fizz" nil nil "Fizz" "Buzz" nil "Fizz" nil nil "FizzBuzz" nil nil "Fizz" nil "Buzz" "Fizz" nil nil "Fizz" "Buzz" nil "Fizz" nil nil "FizzBuzz" nil nil "Fizz" nil "Buzz" "Fizz" nil nil "Fizz" "Buzz" nil "Fizz" nil nil "FizzBuzz" nil nil "Fizz" nil "Buzz" "Fizz" nil nil "Fizz")
```

### reduce

[reduce](http://clojuredocs.org/clojure_core/clojure.core/reduce)も簡単です。 +も関数なので、第1引数に渡すことで、第2引数の0〜10までの和を求めることができます。

```clj
cljs.user=> (reduce + (range 11))
55
```

### filter

コレクションから特定の要素だけ抜き出す[filter](http://clojuredocs.org/clojure_core/clojure.core/filter)も直感的です。
Clojurescriptではデータの塊はレコードとして型定義でき、マップと同様のアクセスができるようになります。

```clj
cljs.user=> (defrecord Person [name age])
cljs.user/Person

cljs.user=> (def persons [(Person. "カミル・ストッフ" 26) (Person. "葛西紀明" 41) (Person. "ペテル・プレヴツ" 21) ])
[#Person{:name "カミル・ストッフ", :age 26} #Person{:name "葛西紀明", :age 41} #Person{:name "ペテル・プレヴツ", :age 21}]

cljs.user=> (filter #(> (:age %) 40) persons)
(#Person{:name "葛西紀明", :age 41})
```
簡単にレジェンドを見つけられます。

## Array Functions

### first, rest関数

はい。これunderscoreのfirst, restと全く同等で、そのまんまcar, cdrです。

```clj
cljs.user=> (defn sum-all [li] (if (empty? li) 0 (+ (first li) (sum-all (rest li)))))
#<function sum_all(li){...}>
cljs.user=> (sum-all (range 11))
55
```

### partition関数

Clojurescriptにも、[partition](http://clojuredocs.org/clojure_core/clojure.core/partition-by)関数(ファミリー)はありますが、ちょっと違う動きをします。コントロールブレイクのようなことをやるために使われます。
underscoreのpartitionのようにコレクションをふるいにかけるようなことやるには、[group-by](http://clojuredocs.org/clojure_core/clojure.core/group-by)を使ってこんな感じでしょうか。

```clj
cljs.user=> (vals (group-by odd? (range 11)))
([0 2 4 6 8 10] [1 3 5 7 9])
```

### zip関数

zip関数そのものは標準の関数にはありませんが、[interleave](http://clojuredocs.org/clojure_core/clojure.core/interleave)と[partition](http://clojuredocs.org/clojure_core/clojure.core/partition)を関数合成することで作り出すことができます。

```clj
cljs.user=> (def zip (comp (partial partition 3)  interleave))
#<function (x,y,z,var_args){...}

cljs.user=> (zip (range 1 4) ["hoge" "fuga" "piyo"] [10 20 30])
((1 "hoge" 10) (2 "fuga" 20) (3 "piyo" 30))
```

## Function Functions

### partial

おっと、zipのところでさりげなく使ってしまってました。ClojureScriptでも[partial](http://clojuredocs.org/clojure_core/clojure.core/partial)で部分適用ができます。

```clj
cljs.user=> (defn g [greeting name] (str greeting name))
#<function g(greeting,name){...}

cljs.user=> ((partial g "Hello, ") "kawasima")
Hello, kawasima
```

プレースホルダーの機能は無いので、先頭の引数をあとで適用することはできません。それをやりたい場合は、関数リテラルを使って、次のように書けば同じことが可能です。

```clj
cljs.user=> (def g' #(g % "kawasima"))
#<function g_SINGLEQUOTE_(p115_SHARP_){...}>

cljs.user=> (g' "Hello, ")
"Hello, kawasima"
```
### memoize

メモ化も同じように出来ます。

```clj
cljs.user=> (def fib (memoize (fn [n] (if (< n 2) n (+ (fib (- n 1)) (fib (- n 2)))))))
#<function (var_args){...}>

cljs.user=> (fib 100)
354224848179262000000
```

## Object Functions

ClojurescriptはJavascriptオブジェクト専用の関数は用意されていません。しかし、Javascriptオブジェクトをマップに変換して、マップの関数を適用すること簡単にできます。Javascriptオブジェクトをマップに変換する関数がjs->cljです。逆の、clj->jsもありますが、Clojurescript Web REPLではまだ動かないようです。

### keys関数, vals関数

```clj
cljs.user=> (def table (js/Object.))
#<[object Object]>
cljs.user=> (set! (. table -hoge) 1)
1
cljs.user=> (set! (. table -fuga) 2)
2
cljs.user=> (set! (. table -piyo) 3)
3
cljs.user=> (js->clj table)
{"hoge" 1, "fuga" 2, "piyo" 3}

cljs.user=> (keys (js->clj table))
("hoge" "fuga" "piyo")

cljs.user=> (vals (js->clj table))
(1 2 3)

```

### Javascriptオブジェクトの扱い

以下、ちょっと省略してClojureScriptからJavascriptのオブジェクトを扱う方法です。

```clj
cljs.user=> (let [ws (js/WebSocket. "ws://localhost:8080")] 
         ...  (doto ws 
         ...    (aset "onopen" #(.log js/console "connected!")) 
         ...    (aset "onmessage" #(.log js/console (str "received message:" %)))))
#<[object WebSocket]>
```

このように、Javascriptオブジェクトはネームスペースjsを付けると参照することができます。newするには、ClojureでJavaオブジェクトを作るときと同じく、後ろに"."を付けます。
プロパティを読み書きするには、aset、agetを使います。または`(.-prop obj)`のように-付きで呼びだせばプロパティにアクセスできます。

## パターンマッチ

パターンマッチと似た機能が、[マルチメソッド](http://clojure.org/multimethods)です。

```clj
cljs.user=> (defmulti calc-price (fn [item] (-> item  :quantity second)))
#<[object Object]>

cljs.user=> (defmethod calc-price :lbs [item] (* (:price-per-lb item) (-> item :quantity first)))
#<[object Object]>

cljs.user=> (defmethod calc-price :kg [item] (* (:price-per-lb item) (-> item :quantity first) 2.204))
#<[object Object]>
```

defmultiでディスパッチ関数を書いておき、defmethodでその結果に応じた処理を定義できます。
あとは普通に関数を呼ぶと、マッチしたメソッドが実行されます。

```clj
cljs.user=> (calc-price (Item. "apple" 1.23 '(1.1 :lbs)))
1.353

cljs.user=> (calc-price (Item. "orange" 0.68 '(1.4 :lbs)))
0.952

cljs.user=> (calc-price (Item. "cantaloupe" 0.53 '(2.1 :kg)))
2.4530520000000005
```

Javascriptにパターンマッチを求めて彷徨っている人は、ClojureScriptを使ってみるとよいのではないでしょうか。

## まとめ

このように関数型言語の要素を取り入れたunderscore.jsの機能は、ClojureのSyntaxを利用したClojureScriptを使えば、特に必要ではなくなります。

残念なことに日本語文献は[入門ClojureScript](http://www.oreilly.co.jp/books/9784873116129/)がある程度で、基本的には英語、ソースコードを読む必要があり、万人にオススメできるようなシロモノではありませんが、LisperがJavascriptを書くには選択肢として十分アリではないかと思います。