Closed Synthetica9 closed 5 years ago
Rationals aren't really continuous, and neither are fixed precision floats or fixed point numbers. It's a good idea though, but could maybe use a more accurate name.
Strictly speaking, a set cannot be 'continuous' (in the mathematical sense, only functions and their generalizations are said to be continuous). The closest notion I can think of is that of meagreness, but a name like MeagreReal
, while more correct, seem obscure and hard to discover. (It also doesn't really differentiate integers and rationals...)
Maybe AbstractRational
? Both fixed and floating point types represent rational numbers.
Well integers are rationals as well.
I'm slightly skeptical of expanding this hierarchy too much, as it does add to the cognitive load (not to mention ambiguity warnings).
This same situation holds with any enhanced or extended or otherwise qualified realization of a floating point type. I can't comment on the ambiguity warnings. AbstractFloat <: AbstractRational <: AbstractReal <: Number would be helpful to me, and allow better specificity when protocols come along. Pulling Complex into the hierarchy probably simplifies stuff over time. AbstractFloat <: AbstractRational <: AbstractReal <: AbstractComplex <: Number
@JeffreySarnoff Why should AbstractComplex
be pulled into the hierarchy? What number types are there that are a Number
, but not an AbstractComplex
?
@Synthetica9 Quaternions (used in computer graphics), Dual Numbers (used in automatic differentiation), Dual Quaternions (used in robotics), Split Complex Numbers (used in special relativity) ...
@JeffreySarnoff I suppose those are all valid cases. I'm still not sure if AbstractComplex
would be the right call; but it might very well be. (I also don't think I'm quite qualified to speak on the subject)
Apologies for huge comment, short version at the bottom.
Confession first: I'm the one who made up the original ContinuousReal
name in a discussion about this which resulted in @Synthetica9 posting. It's a sloppy name, mathematically speaking, but there is some justification possible for the choice made intuitively: It'd encompass all T<:Real
admitting meaningfully continuous endomorfisms (meaning the standard topology on the set approximated by T is not discrete).
AbstractReal<:AbstractComplex
seems sane for the same reasons Integer<:Real
is: the subtype relation encodes a subset relation of the underlying sets. A good reason for subtyping, but under that convention, AbstractRational
would correspond to Q... which has Z as a subset, implying Integer<:AbstractRational
, and we're back where we started, as @simonbyrne noted.
What we want here is not an abstract type corresponding to a set, to be subtyped by all types corresponding to subsets. Instead, we want an abstract type corresponding to a set of types, this being the set of all subtypes of Real
closed under /
, to be subtyped by all types in that set (and supertyped by any universally shared supertypes of the elements).
Calling such types AbstractRational
sounds reasonable, since rationals are constructed precisely to be the closure of integers under /
(that is, Q is the fraction field of Z. Which, incidentally, offers a third option, Fractional
)
Given these differences in what subtyping signifies, I don't think I like the idea of having AbstractRational<:AbstractReal<:AbstractComplex
if AbstractRational
is the abstract type we're discussing here. Various reasons discussed below, short version: Neither AbstractReal
nor AbstractComplex
should exist under those names: Either those types don't follow the same naming convention as AbstractRational
, which is confusing, or they do, which has a lot of issues.
Abstract*
The naming convention would be non-uniform if AbstractReal
corresponded to R with subset subtypes and AbstractComplex
corresponded to C with with subset subtypes, while AbstractRational
corresponded to the set of types closed under /
with element subtypes.
The alternative, AbstractReal
and AbstractComplex
following the convention set by AbstractRational
, is probably even worse:
First, something which is not a problem but needs to be noted: it would require AbstractComplex<:AbstractRational && AbstractReal<:AbstractRational
, not AbstractRational<:AbstractReal<:AbstractComplex
,
Second, it would be quite senseless/useless:
AbstractReal
would signify being compact (closed under 'limit'), but such closures have an annoying tendency to be either uncountable, and therefore not representable by any type, or useless, with only eventually constant sequences having limits.
AbstractComplex
would represent algebraic closure, which kind of is possible, but would be better served by a different name, since the algebraic closure of Z also includes, for instance, sqrt(2)+(3/2)im
, which is not a Complex{Integer}
as one would expect.
Mea culpa on ContinuousReal
AbstractRational
sounds good
Fractional
or Fraction
might also work
AbstractReal
and AbstractComplex
are quite problematic if AbstractRational
is chosen, and should not be introduced.
@Garnasha would you mind clarifying: I understand your point that the nature of Float<:Rational is different from Real <: Complex. Forgetting about the name chosen -- or choosing whatever names are best -- do you object to a numeric hierarchy where Integer <: Rational <: Real <: Complex and Irrational <: Real <: Complex?
and if so, why
I dislike Real <: Complex, because Complex is a field extension, and not an essential abstraction of the real numbers. One can invent all sorts of field extensions Foo, and having done that doesn't mean everybody must consider Real a subtype of Foo.
Thank you for the clarification, that makes sense.
I agree. We identify the the reals with a subset of the complex plane in math, but they aren't by construction actually a subset. Similarly with integers and rationals. And UInt8 is not a subset of Int64 even though the set of mathematical values are a subset.
@JeffBezanson the same can be said for Integer <: Real
though, or even Rational <: Real
: both those extension are also one of many possibilities. That's not to say it isn't a valid concern, but it's not open and shut. And while Complex is just one extension, it is by far the most popular algebraic closure of R.
There's a more abstract discussion possible here, which I'm opening a separate issue on because it threatened to become rather off-topic here (the topic here being the introduction of AbstractRational
to encode closure under /
). Short version: a much more liberal approach to subtype specifications might be possible (without upsetting internal representation), thanks to the Julia approach to inheritance, rendering the Real<:SomeExtension
discussion moot.
Never mind, that discussion presupposes support for multiple inheritance, since that makes the type hierarchy a DAG rather than a tree, allowing the addition of a supertype to an existing type. In a tree hierarchy, it doesn't work.
In that light, Real <: Complex
is probably not the way to go.
Instead of using subsets, this is more elegantly solved with traits. With a trait, you specify what properties a certain type must have (e.g. "closed under division"), and then an arbitrary number of types can implement this trait.
+1 for traits, I think (would have to read more) but are they an official part of the language, or at least going to be?
It's likely that something trait/protocol-like will make its way into the language before 1.0. We'll need to work out the right design, which will take some time.
I ran into this as an issue today, I wanted a function to be allowed to take any number, so long as division was certain to not throw a Inexact Error. I was thinking AbstractField (but that is a confusing name since we use the keyword field already). I wanted to be able to pass Rationals, Floats, Complexs, and DuelNumbers of the aformentioned.
Then I realized that I don't actually have to enforce this in the function signature (though it would be nice to be able to), I can just let the exact error propagate up.
This probably would have been better captured as a discourse discussion, since it's not really an issue, just a question that could be a PR. Closing as not-a-bug, but discussion may continue (or perhaps restart on discourse, where there might be more commenters). As the above commenter (oxinabox) noted, however, duck-typing may be better.
I ran into this issue when trying to make
linspace
spit out fixed point numbers (Courtesy of Jeff Bezanson's FixedPointNumbers.jl). The problem is thatlinspace
asks for anAbstractFloat
. At first, this seems entirely reasonable; given the help availiable in the console:The supertype (
Real
) would be too broad, since it would include integers, and any of its subtypes (BigFloat
,Float16
,Float32
, andFloat64
) would be too restrictive.However, there is no way for anyone to create an type that isn't technically a floating point, yet does support continuous values. We already have such a type, namely
Rational
.I propose that an abstract type
ContinuousReal
be placed as a subtype ofReal
, and thatRational
andAbstractFloat
be made subtypes of it; and that the type requirements of library functions be updated to match where appropriate.