諦めたけど試合終了しなかった件

Jokeカテゴリに投稿したOptionally Typed Ruby、一週間コメントがなかったのでself-rejectをキメたら、カテゴリを変えてまさかの復活。せっかくなので真面目に考えてみることにしたけど、なかなかいいの思いつかない。

指摘のあった「:」がキーワード引数と衝突するのは何か他の記号と置き換えればいいだけとして、duck typing用にメソッドのリストを指定できるような文法を考えるのが難しい。というか、考えるだけなら簡単だけど、不格好で受け入れられる気がしない。

やっぱ無理筋なのかなぁと感じつつ、一応考えてみたことを残しておくので、私の愚考を踏み台に誰かが先に進めてくれると嬉しい。

当初の提案と同じ方向性で

メソッドのリストを指定できるようにするとなると、リストの開始と終了を判断するためには「:」の代わりに何かで挟むしかないのかなと。かりに「<」と「>」で挟むとすればこんな感じ。

def foo(bar <to_s,to_sym>) <String>
  baz <do_something> = Baz.new bar.to_s
  baz.do_something bar.to_sym
end

まぁこれはこれでありな気もするんだけど、実際にはメソッドにもシグネチャってものがあるわけで、そう言うのまで指定するとなると

def foo(bar <to_s(<void>)<String>,to_sym(<void>)<Symbol>>) <String>
  baz <do_something(<Symbol>)<String>> = Baz.new bar.to_s
  baz.do_something bar.to_sym
end

もう何が何だか。さらにHashとかArrayとかのコンテナ系まで考慮すると、この方向性は苦しいのかなぁと。あ、でもrespond_toはシグネチャまで考慮しないことを考えると、ここでもメソッド名だけを考えるのでいいのかも。

インターフェース

メソッドのシグネチャのリストを指定するのが難しいなら、それらだけを保持する「なにか」があればいいんじゃないか。例えばそれをinterfaceとして

interface TosTosymer
  to_s(<void>)<String>
  to_sym(<void>)<Symbol>
end

interface DoSomethinger
  do_something(<Symbol>)<String>
end

def foo(bar <TosTosymer>) <String>
  baz <DoSomethinger> = Baz.new bar.to_s
  baz.do_something bar.to_sym
end

んー、字面はすっきりしたけど、記述量が増えすぎてきつい感じ。同じメソッドの組み合わせが何度も呼ばれるならそれに名前を付けるのは悪いことだとも思えないけど、ここまで話を広げると絶対通らないだろうなぁ。

シグネチャ

元エントリに sig という提案が。提案者のThomasは自分が「dullい提案してごめんね、取り下げるわ」って拗ねたコメント書いたときに、「dullじゃないよ」って慰めてくれたし、きっといい奴に違いない。ただメソッド宣言とは別にプロトタイプ宣言みたいなの書くのはやっぱ面倒くさい。で考えたけど、関数定義にオプショナルなrescue節が付けられるように、オプショナルなsig節を付けるのはどうだろう。

def foo(bar)
sig
  <String>(<to_s,to_sym>)
begin
  baz = Baz.new bar.to_s
  baz.do_something bar.to_sym
end

beginがウザいけど例えばこんな感じ。ただ、これだと変数の型が指定できない。DartみたいにIDEへのヒントとして使うならやっぱoptional typeの方が欲しいなぁ。

DbC

ついでに話を広げると sig とか追加するならいっそのこと契約プログラミングできても楽しそう。いや、やったことないけど。

def foo(bar)
precond
  bar.respond_to(:to_s) and bar.respond_to(:to_sym)
postcond
  ret.is_a?(String)
invariant
  pass
begin
  baz = Baz.new bar.to_s
  baz.do_something bar.to_sym
end

コメント

当初提案のoptional typeはただのコメントなわけで、もういっそのことコメントで書いてしまえばいいんじゃないか。ただRubyのコメントは「#」を使った単一行コメントか「=begin ... =end」しかないけど、できれば対象になる変数の近くにコメントを書きたい。ということでこういうのはどうだろう。

def foo(bar #{to_s,to_sym}) #{String}
  baz #{do_something} = Baz.new bar.to_s
  baz.do_something bar.to_sym
end

#{...} は範囲指定のあるコメントと思って欲しい。これならコメントでも許せる気がする。

アノテーション

上のコメントをアノテーションとして実行時にオブジェクトから取り出せたりすると素敵。アノテーションには # より @ を使いたいから例えばこんな感じかなー。

def foo(bar %@{to_s,to_sym}) %@{String}
  baz %@{do_something} = Baz.new bar.to_s
  baz.do_something bar.to_sym
end

キンモー!!

その他

Smalltalkばりにソースコードをメソッドのプロパティにして云々とかいろいろ考えたはしたけど、これ以上は余白が狭すぎる。