2011-12-21
■Scalaのソースを読んでいて辞めてほしいと思った12の機能
えーっと、 https://github.com/joshcough/Compilers このへんのソースを地道に呼んでいる訳ですが、正直、Scalaの機能をふんだんに使っていて、
やめてくれー!っと思っております。
俺は、Scalaマニアじゃないんだ!コンパイラの仕組みを知りたいんだー!なんて思う訳でした。
ということで、辞めてほしいと思った機能をいろいろと書いてみます。
1.traitで多重継承っぽくふんだんに使う
class A extends A1 with A2 with A3
っと
2.traitを後付けでバックエンドを切り替えて嬉しいぜ機能
new A with X86Backend
とかなんとかやると、 AにX86Backendの機能が追加されて嬉しいぜ
ってことらしいのだけど、x86しかないんだから、やらんでいいよ、、、と思うのでした。
3. _:* なにこれって感じなんですけど
可変長引数らしいんだけど、なくても何とかなるので使わなくていいだろうという。
3.foldLeft, map
foreachでなんとかしたらいいような気もするけど、覚えれば大丈夫なのだけど、いつも悩むので、これは覚えるべきか。
4. yield
ストリーミングして云々なのは知っているけど、結局何やってるんだっけ?と悩むので使わなくていいんじゃないかと思うのでした。
timedとかいう関数かなにかを使うといい感じに時間を計ってくれるんだけど、これ本質と関係ないしと思いました。
Scalaなら、タプルやら、Listやらあるので、とくにいらないんじゃないかと。
シンタックスエラーをチェックするのは、ちゃんと作る場合は必要だけど、俺はレジスタ割り付けだけ知りたいのでいらないと思うのでした。
これもなくても作れるのでないほうが読むソース短くなって嬉しいと思いました。
12. implicit conversion
new File("a.txt").readとやってファイル読み込めるぜ
っていうことなんですけど、readFile("a.txt")とかいう関数で読み込みでいいよって思ったのでした。
と、いうことで、来年は、このソースを呼んでちゃんと把握したら、もっと分かりやすいレジスタ割り付けするプログラムを書きたいと思いました。
でもfoldLeftは実際になにが起こるかほんとわかりづらいと個人的には思ってて、使わなくてすっきり書けるなら使わなくてもいいかなと思ったり。
mapは定番ということで覚えたほうがいいんじゃないでしょうか。
でも、そうも言ってられないんですよね
foldLeftについて。
これを使うのが良いかどうかは相対的な問題で、たとえば、
(a) val total = list.sum
(b) val total = list.foldLeft(0)((x, y) => x + y)
のどちらが読みやすいか(sumはScalaの標準ライブラリで定義されています)と言われると、さすがに(a)だろう
と思います(「合計値を取る」というのがよりわかりやすい)。
しかし、たとえば
(b) val total = list.foldLeft(0)((x, y) => x + y)
(c)
var total = 0
for(e <- list) {
total += e
}
で、(c)を見たら、(b)の方がわかりやすい…と思います。もちろん、foldLeft()を知らない、あるいは知ったばかりの人にとって(b)の方が
「読みにくい」のは当然の話です。ただ、逆に一度foldLeft()の仕様を「理解」すれば、そちらの方が読みやすく感じる…はず。
また、読みやすさ以外の面でも、
・foldLeft()は「必ず停止する」事が(ライブラリがバグって無ければ)保証される
・ループするよりfoldLeft()した方が、型に起因するバグをコンパイル時にチェックしやすい
というメリットはあります。
foldLeft() 対 map() について言えば、前者の方がより汎用的で「力の強い」関数(foldLeft()があればmap()を実装できる)なので、
foldLeft()を使わずにmap()で済むなら、そちらを使うべきだと考えます。
ちなみに、foldLeftはScalaとかの関数型言語の
専売特許というわけでもなく、Smalltalk/Rubyではinject、Perlではreduce、といった形で、いわゆるLL(?)にも普通に入っている
もので、知っておいて損は無い気がします。