ある作りたいものがあるのですが、まずは今ある中で良いとされているモノや設計思想を知っておこうと考えました。そのためには最低でも1,2ヶ月ぐらい腹をくくってそれに取り組む必要があると思います。
というわけで趣味と実益を兼ねて、しばらくRuby on Rails、そのために触ったことがないRubyを書いてみようと思います。
まずは今知っているもの、使いたいものをRubyでどう使えるか、を調べてみます。以下はそのメモです。
REPL
irb
関数/メソッド呼び出し
トップレベルでdefした関数はfoo(a, b) 又は foo a, bで呼び出せる。
メソッド呼び出しはreceiver.method arg1, arg2の形。
トップレベルでのdefはKernelクラスへのインスタンス/クラス両メソッド追加で、レシーバを省略できるだけ。プライベートメソッドになるのでレシーバ指定して呼び出すのはやや面倒?
foo、barともにトップレベル定義メソッド(関数)ならfoo bar xでfoo(bar(x))と右結合になる。
返り値
最後の式。又はreturn xで返せる。
lambda/block/第一級関数
Smalltalkに似た{|a, b| ...} 又は do |a, b| ... endという形のブロックが使える。
ブロックはProcクラスのインスタンスで、使うためにはf.call 1, 2などのようにcallメソッドを呼ぶ必要がある。これはSmalltalkの感覚に近い。Schemeの関数の感覚ではない。
ブロック表記がそのままブロックという値として扱えるわけではなく、Proc.new {...}やその省略形proc {...}とlambda {...}で明示的に生成する。
def foo (f) f.call 1, 2 end foo proc {|a, b| p a; p b}
ただし1つだけメソッドに渡す場合は、Proc.newやprocを省略して、def foo (&block) block.call endのように&で受け取ったり、それも省略してyieldというキーワードで直接呼べる。引数も渡せる。
def foo yield 1, 2 end def bar (n) yield n, 2 end foo {|a, b| p a; p b} bar (1) {|a, b| p a; p b} # ブロック以外の引数がある場合は括弧が必要
トップレベルで定義した関数名を手続きオブジェクトとして直接渡すのは面倒っぽい。とりあえずSmalltalk的に考えると良さそう。
末尾呼び出し最適化
MRIには無いっぽい?
メタクラス
Smalltalkと違ってMetaclassクラスは無く、Classクラスがそれ自身のクラスになって循環している。
あれ、じゃあクラスメソッドはどこに属してるんだろうと思ったけど、Classクラスの無名のインスタンスを作って継承ルートに挟んでるのかな
トレイト
無いっぽい、mixinやモジュールを学ぶ必要がある
コレクションとmap/reduceやcollect/injectなど
Enumerableモジュールを何かしたやつにあるっぽい。
Smalltalkと比べて
- map/collect, select, rejectはそのままな使い方
- reduce/injectは
inject (init) {...}かinject {...} - find/detectはifFound受け取れるやつない、残念
シンボル
:name。
同値/同一性判定
同値には大抵==、同一性にはequal?メソッドを使う。Schemeと逆なので注意しよう。
"foo".equal? "foo" # => false "foo" == "foo" # => true
記号メソッドの呼び出しについて
:foo.== :foo # => true
ウオッ
ローカル変数
定義無しでいきなり使える。危なくない……?小文字又はアンダースコアで始める。
インスタンス/クラス変数参照
インスタンス変数は@、クラス変数は@@を前につける。
クロージャ
トップレベルであれこれするのは避けたほうが良さそう。メソッドの引数とローカル変数では素直に使える。
メソッド内関数(メソッド)定義
レキシカルスコープじゃない……?どうも外側のメソッドが属してるクラスに属するみたいだ。避けたほうが良さそう。
現時点での感想
許容可能なLisp、特にLisp-1のつもりで書くのは危なさそう。反対にSmalltalk-80との根っこの部分での違いは今のところ感じない。