« オンラインのリーダーシップとは何だろうか | トップページ | ソフトウェア・ファーストの感想 »

2020/04/07

「Rubyのしくみ」を読んだ後のRubyの感想

Rubyのしくみ」の感想とRubyの感想をラフなメモ書き。
主張は特になし。
間違った点は後で直す。

【参考】
書籍紹介『Rubyのしくみ Ruby Under a Microscope』

『Ruby のしくみ』を読んだ - blog.kymmt.com

『Rubyのしくみ -Ruby Under a Microscope』の読書メモ<第1~5章> - kenju's blog

『Rubyのしくみ -Ruby Under a Microscope』の読書メモ<第6~7章> - kenju's blog

『Rubyのしくみ -Ruby Under a Microscope』の読書メモ<第8~9章> - kenju's blog

『Rubyのしくみ -Ruby Under a Microscope』の読書メモ<第10~11章> - kenju's blog

『Rubyのしくみ -Ruby Under a Microscope』の読書メモ<第12章> - kenju's blog

Rubyの定数探索、instance_evalやmodule_evalの挙動、特異クラスの性質について、分かったような気がしないので、「Rubyのしくみ」を買って読んでみた。
Rubyのしくみ」の中身はかなりDeep。
まだ全部は分からない。

RubyがYARVというVMベースのインタプリタ言語であることも、この本を読んで初めて知った。
JavaがJavaVMで動いているのと似ているが中身は全く違う。

Rubyのしくみ」を読んで、良かった点と自分の気づきをメモしておく。

【1】下記の章が読みたかった部分だった。

第5章 オブジェクトとクラス
第6章 メソッド探索と定数探索
第8章 Lisp から借用したアイデア
第9章 メタプログラミング

Rubyは、他のプログラミング言語に比較すると、ほぼ完璧なオブジェクト指向言語と思うので、メモリ上にオブジェクトやブロックがどのように実現されるか、というイメージがないと、本当に理解できた気にならない。
ソースコードを解釈したイメージの絵を多用しているので、理解しやすくなっているのは良い。

【2】「Rubyのしくみ」を読んで、include/prepend/refinementsの仕組みが腑に落ちた。

Rubyのモジュールは、内部ではクラスと同じ。

includeの実装は、インクルードしたモジュールをクラスとしてコピーして、継承チェーン上に挿入するイメージ。
prependの実装は、prependを含むクラスをoriginクラスとしてコピーして、継承チェーンの最下部に挿入するイメージ。
prependを使えば、簡単にオーバーライドできるので、エイリアスメソッドチェーンを多用する必要もない。

refinementsも、refineしたクラスを無名クラスとしてコピーして継承チェーンの最下部に挿入するイメージ。

【3】Ruby特有の仕様がある点に注意する。

RubyはJavaと違って、インスタンス変数とクラスインスタンス変数は明確に異なる。
それぞれインスタンスメソッド、クラスメソッドでアクセスして編集する。

Javaと違って、オブジェクトは必ず特異クラスも持つ。
クラスメソッドは、該当クラスの特異クラスに住んでいる。
もちろん、特異クラスにあるクラスメソッドとインスタンスメソッドは異なる。

Rubyのメソッド検索は、継承チェーンを下から辿ってマッチするまで探す。
マッチしなければ、method_missingで補足されて、NoMethodErrorになる。
他方、動的プロキシでは、method_missingでゴーストメソッドを補足して、別メソッドに変換してしまう。

Rubyの定数検索は、まずレキシカルスコープで定数を検索し、その次に継承チェーンで定数を検索する。
レキシカルスコープで定数検索するロジックは、Module.nestingの順に検索すること。
この仕組みを知らずに何度も引っかかった。

ブロックはRuby特有の書き方の気がする。
Rubyのブロックに慣れたら、JavaScriptやPythonよりも直感的で、読みやすく感じる。

メタプログラミングはRubyの真骨頂。
JavaやC#がXMLを多用するのとは違い、Rubyは内部DSLで全てRubyで実現できるのは素晴らしい。
JavaやC#が嫌になる時は、XMLばかり書かされている時と同じだから。

instance_evalもclass_evalも、evalと仕組みは一緒。
但し、処理は異なる。
instance_eval内のメソッド定義(def~end)は特異メソッドになるので、インスタンスメソッドを定義したい場合は動的メソッド(define_method)を使う。
一方、class_eval内のメソッド定義はインスタンスメソッド。

また、instance_eval do~endと、instance_eval(<<-EVAL):ヒアドキュメントでは、定数検索が異なる。
Module.nestingで出力してみると、前者は[]なのでトップレベルの定数、後者は[特異オブジェクト]なので、特異オブジェクトに属する定数になる。
この点も知らずによく引っかかった。

Proc#[]という書き方があるのもびっくりした。
改訂2版 Ruby逆引きハンドブック」を読んでいたら、メソッド呼び出しに似せるためにProc#[]のように書く、という説明があって、なるほどと理解できた。

【4】Rubyには異音同義語が割と多くある。

mapとcollect、injectとreduce、selectとfind_all、detectとfind。
lengthとsize, count。
他にもいっぱい。

aliasはキーワードで、alias_methodはModuleのprivateメソッド。
undefはキーワードで、undef_methodやremove_methodはModuleのprivateメソッド。
module_functionで、メソッドを関数化できる。

RubyはVerUpで言語仕様がどんどん機能追加されているので、その歴史による経路依存性のせいかもしれない。

【5】Ruby初心者が悩みがちな特殊記法も多い。
慣れれば、短く書ける。

Ruby初心者を脱した人が悩みがちな、ちょっと特殊な記法・演算子・イディオム - Qiita

*args:配列で受け取る可変長引数。splat引数。

**opts:ハッシュで受け取る可変長キーワード引数。

*変数:splat演算子で配列要素を展開する。

&block:ブロック引数。

&:method_name:メソッドに & とともにシンボルを渡すとto_proc が呼ばれて Proc 化され、それがブロックとして渡される。

なお、メソッドオブジェクトを呼ぶ場合、&method(:メソッド)を使う。
動き方は、(1)Object#method でオブジェクトにあるメソッドをオブジェクトにする。(2)メソッドオブジェクトには、Object#to_proc があるので、&演算子でブロック引数に渡すことができる。(3)メソッドをブロック引数に渡すことができる、になる。

_変数:使わない変数名を宣言。だから、Rubyのローカル変数は「英小文字かアンダースコア(_)始まり」になる。

||=:通称「nilガード」。初期化イディオムとして使う。

<=>:宇宙船演算子、UFO演算子

->:lambda式で用いる「lambdaリテラル」や「アロー演算子」と呼ぶ。

「::」:スコープ演算子。ネームスペースで使う。定数探索でよく出る。

=>:「ハッシュロケット」と呼ばれ、ハッシュリテラルを書く際にキーと値を区切るもの。

「&.」:呼称はSafe navigation operator、通称ぼっち演算子。obj&.my_method形式でのメソッド呼び出しで、objがnilでなかったらmy_methodを実行。

「!!」:すべてをtrueかfalseにする。


|

« オンラインのリーダーシップとは何だろうか | トップページ | ソフトウェア・ファーストの感想 »

Ruby」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



« オンラインのリーダーシップとは何だろうか | トップページ | ソフトウェア・ファーストの感想 »