エンジニアの中出です。
私は元々、C/C++やJava等のコンパイラ型言語をこれまで扱ってきましたが、マネーフォワードに入って、ついにRuby(Ruby on Rails)に触れる機会を頂きました。
Ruby on Railsの学習を始めてまだ2ヶ月弱なのですが、すでに色々な学びがありました。 今回はその中でも言語としてのrubyについて、今更ながら衝撃を受けたことを書きたいと思います。
1. 本当にすべてオブジェクト
rubyは紛れもなくオブジェクト指向なプログラミング言語です。
一般的にオブジェクト指向な言語では処理や操作の対象をオブジェクトとして扱おうとし、すべてがオブジェクトとして扱える世界を善しとする文化があります。というよりそれがオブジェクト指向と言えます。
一方、多くのオブジェクト指向な言語においても数値等のデータ型はオブジェクトして扱わない事が多いと思います。 javaであればよく批判の対象ともなるint(とオブジェクトとして扱う場合のラッパークラスとしてのInteger)、後発のC#ではこれは改善されているが値型、参照型という別の概念が登場していたりします。
これはわかり易さ、あるいは一貫性のとれた美しさと処理性能/効率のトレードオフの結果と言えます。 例えば、forループのループ変数などは頻繁に値が変更されます。値を表現するクラスはimmutableとしたいという別の要求を優先するとするならば(そしてrubyは数値型はimmutable)、ループの度に新しいオブジェクトのインスタンスを生成するということになり、決して効率的とは言えません。
またメモリー管理の観点でもオブジェクトのインスタンスはスタック領域ではなくヒープ領域に配置されます。(C++の自動変数等の一部例外を除いて) スタック領域で管理できるデータは、スタック領域で管理した方が、無駄なアドレス参照も発生しないため性能的にも有利ですし、最近の言語(処理系)はたいていはガベージコレクタを実装していますが、スタック領域に格納されたデータはGCの対象にもならないためエコと言えます。
おそらく、歴史的に多くのオブジェクト指向言語の作者が同じ選択肢に直面して、結果的に性能面のアドバンテージを優先し妥協し続けてきたと思われる、「本当にすべてオブジェクト」をrubyは実現しています。
2.オープンクラス
rubyでは言語仕様として、すでに定義されているクラスを後から拡張したり、振る舞いを変えてしまうことが出来、その機能はオープンクラスと呼ばれているようです。
具体的には定義されていないメソッドを追加したり、定義済みのメソッドの再定義(書き換えが)出来てしまいます。 これだけ聞くと、それってクラスを継承して拡張するという意味ですか?と聞きたくなりますが、オープンクラスでは、継承ではなく既存のクラスを直接拡張してしまいます。そしてその行為はモンキーパッチと呼ばれ、その言葉の響きにルパン三世を見ているが如くのワクワク感、あるいは胸騒ぎを感じてしまいます。
確かにこれまでも言語が標準で提供しているライブラリ内のクラスやサードパーティー提供のライブラリ内のクラスに対して、バグを修正したかったり、挙動を変えたいと思うことが時々あり、クラスの継承やデコレータパターンなどで対応できないかを検討するものの、多くの場合はシンプルに対応することが出来ずにごちゃついてしまったり、運が悪いと禁断の外部提供ライブラリのソース書き換えに至ってしまう場合がありました。
そんな思いをした多くのプログラマーを救ってくれるのがオープンクラスなのです。 確かに、オープンクラスもまた禁断の果実であることが容易に想像されます。クラスの定義が別の場所で変更される。しかも元のクラスの定義にはモンキーパッチされた形跡はなにもありません。またモンキーパッチされたクラスを利用するプログラマーもそれがモンキーパッチされているかどうかなかなか気づけません。正確には実行してみるまで本当に定義が変更されるかどうか確定しないのです。
しかしそれでもオープンクラスはプログラマーにとって強力な武器になるでしょう。
3.ゴーストメソッド
黒魔術とも呼ばれるゴーストメソッド。またまた胸騒ぎがします。
ある日、こんな経験をしました。このソースのここで読んでいるメソッドの仕様を確認したい。よし、ソースをgrepしてみよう。 しかしメソッドは見つかりませんでした。オカシイ。このメソッド名からして明らかに、このシステムで固有の物。動かしてもエラーにならない。どこかに存在するはず。私は自分のgrepが間違っていると判断して、何度もgrepをやり直しました。。。
rubyでは定義されていないメソッドを呼び出すことが出来ます。 これもカラクリを知らない人が聞くとなんのことだかさっぱりわかりません。
rubyではメソッド呼び出しを行ったが、継承元を最後(Object)まで遡っても呼び出されたメソッドが見つからない場合に、method_missing()というメソッドが呼び出されるようになっています。そしてmethod_missingは第1引数で呼び出そうとしていたメソッド名、第2引数で呼び出し時に与えられていた引数を受け取ります。後はmethod_missingを再定義して受け取ったメソッド名と引数から適切な処理を行うように実装すれば何でも出来ちゃうというカラクリです。
ゾクゾクします。悪魔になったような気分にもなります。そしてこれもまた禁断の果実であることが容易に想像されます。 世界中に私と同じ経験をした人がいることでしょう。 そしてシステムの保守で、method_missingを利用していることによる調査の難航は起こり得るでしょう。 しかしこの機能は多くの柔軟性をもたらし、実際にrailsではこの技術をふんだんに利用しています。
適切に使えば、オープンクラス同様にプログラマーにとって強力な武器になりそうです。
以上、特に衝撃を受けたrubyの言語仕様3点について感想を書かせていただきました。 強く感じたこととして、rubyは多くの言語が踏み越えてこなかった一線を簡単に踏み越えてきている非常にスリリングで非常に自由な言語であるということが分かりました。
まだまだ勉強中ではありますが、今後も楽しく付き合ってきたいと思います。
最後に
マネーフォワードでは日々さまざまな挑戦をしていきたいエンジニアを募集しています。 みなさまのご応募お待ちしております!
マネーフォワード採用サイト https://recruit.moneyforward.com/
Wantedly https://www.wantedly.com/companies/moneyforward
【マネーフォワードのプロダクト】 ■家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』:https://moneyforward.com/ ■クラウド型会計ソフト『MFクラウド会計』:https://biz.moneyforward.com/accounting ■クラウド型請求書管理ソフト『MFクラウド請求書』:https://invoice.moneyforward.com/ ■クラウド型給与計算ソフト『MFクラウド給与』:https://payroll.moneyforward.com/