Rust なのか Go なのか..

ひとりごと

Python に加えて何か別の静的言語を学習しようと思った。Python のみの理解では処理速度的にちょっと辛いことがある。

そうすると Elixir とか Go とか Rust とか出てくる。Go か Rust かで検討していて、どうも色々読んでると Rust に軍配が上がりそうな気もしつつ、 Go かなと思ったりもする。

技術的なトレンドとかはよくわからない。Go は登場してから Rust に比べれば急激に人気を増やしたような気配がある。後発の Rust はじっくり、じんわりな感じだ。

Go が一気に Rust を突き放して駆逐しそうに見えるけど、ここ1年に限定すれば Rust と Go は横ばい。数字だけ見ると、このまま安定してお互い共存しそうな気配がある。

実行速度は Go に比して悪く無いと言うか早い.. 本当にそうなのか.. 機能削った Go の方が早いんじゃ無いのか.. あまりちゃんと読んでない。
The Computer Language Benchmarks Game

Go は色々機能を削って作られた言語らしい。Google はサービスの機能を意図的に削除したりする傾向にある。GAE は JOIN を削ったし、Chrome は綺麗さっぱりいろんなボタンをブラウザから削った、検索サイトのトップ画面も Yahoo! やら msn とは違う、検索欄だけのページを設計したりした。*1

リファクタリングしていて関数やらコードを削れたときは、結構気持ちいいものだし、誰が書いても同じようなコードになるというもちろんそれはすごく惹かれる..

けど、やや辛い時がどうしても生じてくる気がする.. GAE みたいに。

ある程度先が、開発するものの全体像が見えていれば採用もできるかもしれないけど、例えば Python の処理速度では対応できなくなったから Go に書き直すみたいな。
Why we switched from Python to Go

特にこの Go から Rust に移行した人が書いた記事とか読んでると、そんなにすごいのかとか思っていまう。

私は何かを書くたびに古くて濁ったプログラミング知識の膜が自分の目からキレイに拭き取られていくのを感じました。その光を見てしまったらこの約束の地に降り立ってしまったらもう引き返すことはできません。
RustとDNSの1年


ただ、抽象化して簡潔にできることは、必ずしもいいことだけではない。 色々な書き方ができてしまうと言うのは、それなりに弊害もあったりする。

コードが理解しづらい
  4. コメントなしに低レベルの最適化が施されている
  5. コードが賢すぎる
コードが追いかけづらい
  4. 全てが抽象化されすぎている
まずコードの可読性を最適化しよう

例えば Python は、「インデントを強制して誰が書いても同じような」と主張されてるよりは、意外といろんな書き方ができる。もちろん、どんな言語だって書こうと思えばいろんな書き方ができる。「同じような」なんて言うこと自体若干怪しい。

ただ、意外といろんな書き方ができる一つの要因としては Python が高機能であることが挙げられると思う。これらの機能を使うことで、抽象化を上げて、より簡潔な記述で記載することができる。反面、その機能を使うか使わないかで、書き方に幅が出てくる。

for 文を iterator 機能を使って回すのか、それとも実直に list を生成するのか。all 関数で条件文を書くのか、それとも実直に and で全部書き上げてしまのかとか。

"""矩形の等号 == を表現する書き方について考える."""


def sample_code():
    # 書き方1 実直に..
    print(eq(Rectangle(1, 1, 2, 2), Rectangle(1, 1, 2, 2)))
    
    # 書き方2 Python の機能をフルに使って..
    print(Rectangle(1, 1, 2, 2) == Rectangle(1, 1, 2, 2))


# こんな矩形のクラス
class Rectangle(object):
    def __init__(self, x1, y1, x2, y2):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2


#
# 書き方1 実直に..
#
def eq(rectangle_a, rectangle_b):
    return \
        rectangle_a.x1 == rectangle_b.x1 and \
        rectangle_a.y1 == rectangle_b.y1 and \
        rectangle_a.x2 == rectangle_b.x2 and \
        rectangle_a.y2 == rectangle_b.y2


#
# 書き方2 Python の機能をフルに使って..
#     all 関数, zip 関数, list 内包表記
#     yiled 文, operator overload...
#     メソッドの動的な追加
#
def __eq__(rectangle_a, rectangle_b):
    return all([a == b
                for a, b in zip(rectangle_a, rectangle_b)])

def __iter__(rectangle):
    yield rectangle.x1
    yield rectangle.y1
    yield rectangle.x2
    yield rectangle.y2

Rectangle.__eq__ = __eq__
Rectangle.__iter__ = __iter__


#
# Execute the sample code.
#
if __name__ == '__main__':
    sample_code()

書き方2は、本来 iterable 出ないものを無理やり iterable にして抽象化の度合いを上げてるので読み辛い。ここでは可読性は置いておいて、書き方に幅があることだけを考察。適切に抽象化したものは、表現も簡潔になり可読性も上がる。yield 文, generator なんかも、うまく使えばコード数を半分以下にすることができる。
9. iterator を自作する4 ジェネレータ, generator


狂気の沙汰だけど Python でも実行速度を求め出すと、稀に低レベルに記述したりし出したりする。関数呼び出しのオーバーヘッドを削るためにベタがきするとか。

$ # 関数を使ったコード
$ python -m timeit "max(2, 1)"
1000000 loops, best of 3: 0.209 usec per loop
$
$ # ベタ書きのコード
$ python -m timeit "2 if 2 > 1 else 1"
10000000 loops, best of 3: 0.0395 usec per loop
$ # 関数を使ったコード
$ python -m timeit -s "import math" "math.floor(4 / 3)"
10000000 loops, best of 3: 0.169 usec per loop
$
$ # ベタ書きのコード
$ python -m timeit "4 // 3"
100000000 loops, best of 3: 0.0132 usec per loop
$ # 書き方1 実直に..
$ python -m timeit -s "from rectangle import Rectangle,eq" "eq(Rectangle(1, 1, 2, 2),Rectangle(1, 1, 2, 2))"
1000000 loops, best of 3: 1.94 usec per loop
$
$ # 書き方2 Python の機能をフルに使って..
$ python -m timeit -s "from rectangle import Rectangle,eq" "Rectangle(1, 1, 2, 2)==Rectangle(1, 1, 2, 2)"
100000 loops, best of 3: 4.33 usec per loop
$

Python を高速化したい。


処理を速くしようとして、こんなこととか、 for 文で書こうかなとか while 文で書こうかなとか、そんなことを気にしだしたらいっそ思いっきり Java などの静的言語に切り替えを検討する時だと思う、あるいは Rust か Go か。

なぜなら、抽象化の一貫性を犠牲にしてまで工夫しても、部分的に見れば何倍も速くできるけど、全体から見れば高が知れていて数%も速くできるかどうか..。でも、言語を切り替えれば何倍も速くなる。

Python は、適切に抽象化具合をあげて短く書くことを前提にしている。何故なら、Python がそういった機能を提供しているし、PEP 8 - Style Guide for Python Code でも 1 行最大 79 文字とごく短く制限されているから。
Python で 1 行を最大 79 文字以内に抑える方法とその理由

低レベルに書き込むことは、PEP 20 - The Zen of Python の言う、たったひとつだけあるはずのストレートなやり方ではないと思う。

何かをやるには、ストレートなやり方がひとつ、
たったひとつだけあるはず
There should be one-- and preferably only one --obvious way to do it.
Python にまつわるアイデア: PEP 20 | Life with Python


Rust がネックになるのは、抽象化の一貫性にばらつきが生じてしまう可能性があること。Go がネックになるのは、どうしても可読性に欠けてしまうこと。それに耐えられなくなるったある日、突然 Rust の光を浴びてしまうのかもしれない。

ただ、学習するっていう意味で言えば、Go と Python という組み合わせは、両極にあっていいのかもしれない..。Go と Python で切り替えて気分転換しながらみたいな。人間、抽象具合をあげて簡潔に書きたい時もあれば、ガリガリ低レベルに書きたい時もある。



でも、もっと Go と対極にあるのは Ruby だと思う..。

昔、PythonRuby を比較して Ruby は、自由に書けるから遊びやって just for fun や見たいな感じで YouTube でディスってる人がいて、炎上してる動画を見たことがある。

でも、楽しいってすごく大事だと思う。もし気の合う人とだけで少人数でプログラミングができるなら、きっと Ruby は楽しい言語だと思う。Rails が出てきて Web サービス組むことに対して Python に比して1日の長が得られたのもそういった所にあるのかなと思う。

Python の framework を探してたときに、ほんの少しだけ CakePHP をかじってたから、当然、インデントを強制して誰が書いても同じようにと歌っている Python の framework も当然、設定より規約的なものだろうと思っていたけど、現実はそうではなかった。

Django は大きすぎて触ってて違和感があって。色々と自由に作れるように、ちゃんと設定してねっていう感じ。でも、そこまで作り込むなら Java なりなんなりに逃げると思うし。別にそんなに大きいもの作らなくていいから、程よく小さいものが作れるのが欲しかった。

だから Rails どうなんだろうって思ってコード見たときは、Rails の光が差し込んできて慌ててドアを閉めた記憶がある。自分の人間的な余裕がなくて Ruby を触れないから..

でも、面白いのは Ruby は自由にプログラミンを楽しもうって言ってたのに、出てきた framework は設定より規約で、反対に Python はそうではない。Django がデカすぎるからって出てきた flask とかは、今度は小さすぎて、自分で好きなものをツギハギして自由に書いてねって感じになっていて、そうじゃないって思った。

お互い歩み寄ってるような感じがして、心地よくプログラミングができるって言うのは、単純にこっちがいいとは言えず、繊細さがあるのかもしれない。極端に規約とかに縛られるのが好きな自分は、やっぱりキチガイなのかもしれない。

プログラミング言語に限らず、自然言語も楽しさとかを元に変化したりする。例えば黒人英語とかはその最たるものだと思う。文法の一貫性よりも音感の良さを取り入れて変化してる。*2
言語変化
黒人英語

さらに言えば人は使う言語によって、思考に影響を受けたりもするらしい。難しくてよくわからない。
言語的相対論
使う言語が「世界の見え方」を決めている:研究結果
言語は思考にも影響を及ぼす、だからRuby開発を選んだ
あんな姉と妹の区別もつかない劣等言語でエロゲができるか

日本語も主語とか目的語を省略したりするから、黒人英語が音感を大事にするのとは違うけど、相手とのなんとなくのフィーリングを共有する、会話そのものを楽しむ言語だと思う。そう言う意味ではプログラミングそのものを楽しむって言う Ruby は、どこか日本語的な言語だなと思ったりする...

結局 Rust か Go かと言うのは、そう言った個人の繊細さの問題なのかもしれない。抽象化して簡潔に書きたいなら Rust だし、低レベルにガリガリ書きたいなら Go みたいな。

Go

Goはオブジェクト指向言語だろうか?
実装継承ってあかんのやな..
Go(golang)についての雑感 | Qiita
Java 使いが Go言語を一日使って思ったこと | Qiita
オブジェクト指向言語としてGolangをやろうとするとハマること | Qiita

ポジティブな記事

Why we switched from Python to Go
または私は如何にして例外するのを止めて golang を愛するようになったか
(go report) Goが本当はすごかったので, 謝罪する

ネガティブな記事

Go言語がダメな理由

Rust

Rustは何が新しいのか(基本的な言語機能の紹介)
立ち位置とか

ポジティブな記事

RustとDNSの1年

ネガティブな記事

見当たらない





*1:SNS系のシステムについては、余分な機能を削り必須の機能に絞ることが、必ずしもいい結果をもたらすとは限らない。蛇足と思われる機能を付け加えたり(mixi の足跡)、あるいは本来必要なはずの機能を削ったりして(Twitter の文字数の制限, 増田の匿名)、コミュニケーションの活性化を測ることができる。SNS では、何をコミュニケーションするかよりも、どうコミュニケーションをするかの方が重要なのかもしれない。とコミュ障が申してます。Google が何故か SNS 系のサービスだと苦戦する傾向があるのは (Blogger, Google+)、こういったところにあるのかなと思ったりする。

*2:全然話は違うけど中国語の動詞には過去形がなかったりする。中国 4,000 年の歴史とは一体何だったのか。かわりに、昨日(昨天)とか時間を表す単語で過去を表現したりする。また過去形ではないけど、完了形(了)は存在する。単純に動詞に活用がない孤立語だからという話でもあるけれど。Wikipeda によると大昔は中国語にも動詞の活用があったらしい。漢字で文章を書いてたら、口語も孤立語になってしまったのやろか。この記事は、中国語じゃなくて英語だけど名詞の格が消えた経緯が書いてあって、すごく面白い。なぜ英語はSVOの語順なのか? 言葉も時代によって大きく変わる。だから「言葉の乱れ」とか「正しい言葉遣い」という言葉は、わいはポジショントークだと思う。「自分たちが」使っている言葉を「正しい」と言ってるのだから。