Feature #21148
openClass proposal: IndefiniteNumeric < Numeric
Description
Suppose someone deals five cards face down from a regulation poker deck, and we wish to reason about the number of aces. We know that that number is one of zero, one, two, three, or four. Therefore, if someone asks "is this number larger than negative one?", the answer is yes even without ever needing to resolve the actual value.
This proposal is inspired by the proposed Enumerable::Lazy::Length
class of Kevin Newton in response to #21135
The IndefiniteNumeric
class would serve as a base class for such values. In particular, it relies on a subclass to define #value
, which returns a definite Numeric
.
class IndefiniteNumeric < Numeric
def coerce(other) = value.coerce(other)
def +(other) = value + other
def -(other) = value - other
def *(other) = value * other
def /(other) = value / other
def <=>(other) = value <=> other
end
It is expected that in particular, <=>
will be overridden for subclasses where #value
might be slow to return.
Usually, we will know more than just that the value in question is Numeric
. It seems appropriate have IndefiniteInteger
and so forth. What is not clear to me is if IndefiniteInteger
should be a subclass of IndefiniteNumeric
or of Integer
. (Such a thing does not appear to be currently possible.) If subclassing cannot be, what about defining #kind_of?(Integer)
to be true
?
Note: with the length of an Enumerable
as inspiration, I would argue that an IndefiniteInteger
might actually value to Float::INFINITY
, so it should NOT define <=>
against plus or minus Float::INFINITY
.
Updated by nobu (Nobuyoshi Nakada) 4 days ago
Student (Nathan Zook) wrote:
The
IndefiniteNumeric
class would serve as a base class for such values. In particular, it relies on a subclass to define#value
, which returns a definiteNumeric
.It is expected that in particular,
<=>
will be overridden for subclasses where#value
might be slow to return.
It sounds like a role of a Module
, not a Class
.
Usually, we will know more than just that the value in question is
Numeric
. It seems appropriate haveIndefiniteInteger
and so forth. What is not clear to me is ifIndefiniteInteger
should be a subclass ofIndefiniteNumeric
or ofInteger
. (Such a thing does not appear to be currently possible.) If subclassing cannot be, what about defining#kind_of?(Integer)
to betrue
?
Ruby prohibits so-called diamond inheritance, and uses modules.
What is the necessity that it should be a built-in Class
/Module
?
Updated by Student (Nathan Zook) 3 days ago
I was not talking about diamond inheritance. I was talking about
Numeric
Float
IndefiniteNumeric
IndefiniteInteger
IndefiniteFloat
Integer
...
vs
Numeric
Float
IndefiniteFloat
IndefiniteNumeric
Integer
IndefiniteInteger
...
which might break things.
But that discussion is mostly moot when talking Class
vs Module
. "Indefiniteness" is clearly a property that might apply to objects in a way that strongly matches the Module
concept. However, as I think about your observation, it seems that I might have been thinking too specifically about the application I was envisioning.
Perhaps my real issue is with the behavior of Numeric
. Subclasses of Numeric
are required to define #coerce
(and encouraged to implement the arithmetic operators) when in many cases, these objects are numbers with additional context. Whether we are talking cats in a box, the number of elements in an enumerable, or the change from a purchase, there is a definite value associated with the object, and once we have that, all of these arithmetic operations, to include coercion, are generally set.
As for "necessity", after thirty years, we're probably passed that. :D For utility, however, I think that this significantly simplifies subclassing Numeric
.
Assuming this idea is desirable, I see three approaches:
- New module,
Value
with the above method definitions. This could be included by new subclasses ofNumeric
as desired. - New subclass of
Numeric
,Value
, with the above method definitions. - Modify
Numeric
to check for the presence of#value
in objects where#coerce
is not defined, and using the result as described.
The example class Tally
in the documentation strikes me as wrapping a representation around the whole numbers. As such, it is natural coerce other values into Tally
objects, but for the cases I envision, it goes the other way.
The proposed method definitions fill out contract for Numeric
subclasses. While it might be reasonable to include these definitions in a class which does not descend from Numeric
, such a class would not gain the functionality from Numeric
that these definitions enable.
Between subclassing Numeric
and changing Numeric
, I feel nervous about changing behavior, even if it means no raising against code that currently fails a contract.
Also, I am not here to ask for the core team to implement this feature. I am looking for support for and guidance on a feature before I develop the code and submit it. (Unless that would be even more work for the core team...