台風一課
捜査一課みたいなノリで.
気合を入れてRubyの勉強中.
3.to_s() → "3":String 3.to_s(1) → ArgumentError 3.incorrect_method() → NoMethodError
とかやって,それぞれのエラーを確認した後,いきなりRubyソースコード完全解説とか読み出すわけで,これはかなり特殊な勉強のしかたかもしれない.
まず,パーサを書きたいわけなんだけど,これが面倒っぽい*1.parse.yは見たけど・・・でかっ.読みたくないなあ.そしてこれをocamlyaccで書き直すのが面倒くさい.念のため,誰かが書いてないかGoogleしてみたけど,引っかからない.
どちらかといえばどうでもいいんだけど*2,ocamlyaccでダメだったらCでがんばるしかないんだろうか.それは,やだなあ・・・
引き続き,Rubyについて考える.目標は,Rubyプログラムの型の整合性を保証することだ.ここで,型の整合性がとれているということを次のように定義する.
型の整合性がとれている ⇔ プログラムを実行したときにNoMethodErrorが発生しない*3
ちょっと適当すぎるかもしれないが,まつもとさん自身
しかし、Rubyのような言語にとって継承は単なる実装の共有が目的ですから、興味があるのは「そのクラスのインスタンスが持っているメソッドのリスト」のはずです。
とおっしゃってるので,まあ問題ないだろう.これならsub typingの理論とか,持ち出す必要はなく,単純にメソッドの集合の関係を考えれば良い.
さて,簡単な例から.
def hoge(o) o.huga() end
というメソッドは,引数0のhugaというメソッドを提供するオブジェクトならば,なんでも受け取れる.なので,適当な書式だけど,
hoge : {huga: () -> 'a} -> 'a
みたいな型みたいのになる.ここで,1みたいなhugaというメソッドが実装されていないオブジェクトをhogeに渡そうとすると,型エラーが報告されれば良い.
しかし,簡単なのは上の例くらいのものだった.
def hoge(cond_expr) if (cond_expr) return 1 else return "a" end end
となると,話は複雑になる.この場合,cond_exprが真なら,
hoge: Boolean -> Integer
だし,偽ならば
hoge: Boolean -> String
になってしまう*4.
ていうか,こういうパターンさっきまで考えてなかった.ずぎゃー
ずぎゃーとか言ってても始まらないので,もう少し考えてみる.
そもそも,どういう場合に上のようなコードを書く必要があるのか.パッと思いつくのは,FactoryMethodだ.その場合は問題ない.ifのbodyとelseで,返されるオブジェクトのメソッドの共通部分を取れば良い.
class LionAnimal .... def lionSpecificMethod() print "Gaooo" end def hoge() print "Lion" end end class FishAnimal .... def fishSpecificMethod() print "Pityan" end def hoge() print "Fish" end end def newAnimal(type) case type when "Lion": return Lion.new() when "Fish": return Fish.new() end end
みたいなプログラムでは,newAnimalの返り値はhogeを実装していることは保証できる.FactoryMethodの範疇で考えれば,これだけで問題ない.よかったよかった.
しかし,ここで例えば
def hoge(animal) if animal.type="Lion" animal.lionSpecificMethod() #1 else animal.commonMethod() #2 end end
なんてプログラムを考えると,また話はややこしくなる.#1を考えると,animalは,lionSpecificMethodとtypeを実装していなければならないことになる.しかし,#2を考えるとanimalはcommonMethodとtypeを実装していなければならないことになる.
ん?なんか変だぞ.
最初の例で問題にしているのは,メソッドが返すオブジェクトが満たしていることを保証できる条件で,次の例で問題にしているのは,パラメータとして渡されたオブジェクトが保証していないとまずい条件だ.
と,この辺で混乱してきたので,また今度.