Siena. です。

かなり悩みましたが、自分の中では少し整理できたように思いますので。

▼ [ruby-dev:17228] < Yukihiro Matsumoto さん

[ruby-list:35279] で Ruby は大クラス主義という御指摘もありましたが、
それを踏まえても、やはり Range に連続・離散区間の両方を扱わせるのは
避けたいという見解に (今のところ) 落ち着いています。

とりあえず、そう考える理由を挙げておきたいと思います。

》ちょっと考えてみました。で、結局問題があるのは
》
》  member?, include?step, eachおよびeachを元にしたEnumerableメソッド

Enumerable のメソッドは、Enumerable としての挙動をとって欲しいです。
クラス単体で見た場合に Enumerable と異なる挙動であった方が自然であっても、
Enumerable であるとみなして扱う時に Range を特別扱いしないとなりません。
Enumerable その存在が大きいため、例外的なものがあると抽象レベルで
扱う事を難しくしてしまいます。更に Range は組み込みクラスなので、
余計に整合性を損なってしまうのではないかと危惧します。

例えば、Enumerable なオブジェクト群 [ e_1, e_2, ... ] があった時に、
あるオブジェクト val で include? val == true となる e_i において
each するといった場合などに、含んでいると判定されたのにもかかわらず
each 中に出現しないという状況が発生してします。これを避けるために、
Range のためだけに to_a.include? val とするのも本末転倒と感じます。

連続区間としてのメソッドを Enumerable のメソッドとは異なる
名前で定義する事も、選択肢としてはありえます。しかしこの場合、
Enumerable のメソッド名が汎用的で連続区間としてのメソッドの
名前と競合しやすいため、不自然/冗長な名前になる、似た名前の
異なるメソッドが並存する、などの問題が出てしまうと思います。
これが自然に解決できるようならば一つのクラスにまとめても
良いですが、それは非常に困難ではないかと想像しています。

》  * member?, include?
》
》    Numericであるか、succが定義されていなければ範囲によるチェックを行う。succが定義されていれば繰り返してみてその要素があるかないかをチェック。

というわけですので、Numeric であっても、離散区間
(離散集合) としてのメンバシップ判定を行なって欲しいです。
そうでないならば、Range は Enumerable と似て非なる
インターフェイスを持つ事になり、好ましくないと思います。


上の問題と刻み幅を持たせるという事は独立に
考えられるので、こちらは別途考えたいと思います。
ちょっとしつこいようで申し訳ないのですが、
もう少しお付き合いいただけたら幸いです。
以下、便宜上、刻み幅を diff (デフォルトは 1) と表記します。

》  * step, eachおよびeachを元にしたEnumerableメソッド
》
》    eachはsuccが定義されていなければTypeError例外(だからFloat のRangeには使えない。これにより全部のEnumerableメソッドは要素がsuccを持つ必要がある。
》
》    stepはNumericならstepを毎回+する(Floatでは誤差が蓄積するが、ここでは無視)。それ以外ならsuccを使って定義する。

Range#each で、first と diff の加算が可能であれば diff ずつ進み、
そうでなければ diff (但し、FixNum) 回 succ しつつ進む、という
挙動は、これらの判定ができるならば不自然な定義とは思えませんので、
導入を検討していただきたく思います。これらの判定は、上記の
まつもとさん提案のものと同様 (or 改善版) で良いと考えています。

また、Range#each は Range#step( 1 ) 相当とみなされると思います。
つまり、Range#step( n ) は、first と diff の加算が可能であれば
diff * n ずつ進み、そうでなければ diff * n 回 succ しつつ進む、
という挙動で定義できると考えています。


改めて次を提案したいと思います。
* 純粋に連続区間を扱うために Interval といったクラスを
  導入して、連続・離散区間を扱うクラスを分ける
* Range は刻み幅 (デフォルトは従来相当の 1) を持つ範囲として拡張する
* Range#interval, Interval#range( diff ) といった
  メソッドで互いを抽出できるようにする

Range#=== を離散区間としてのメンバシップ判定に従わせるか、
従来との互換性を考えて連続区間としてのメンバシップ判定に
従わせるかは、議論のあるところだと思います。
上の提案の場合は、少なくとも、従来の Range#=== は
Range#interval.=== (書き方が変 ^^;) 相当とみなせます。

この提案は現実的ではない、もしくは Ruby の方針に合わないでしょうか。
Interval 相当はユーザライブラリとするのでも良いですが、
少なくとも Range は上記のようになっていて欲しいと思います。

# また長くなってしまった... (--;

---
Siena. <mailto:siena / cr.chiba-u.ac.jp>