Interface for subtypes of Core.Real

Many packages subtype Core.Real to take advantage of the generic methods in Base and interface with third-party packages. However, while similar supertypes like Base.Array have their interface defined in the Julia manual no such description exists for Real.

Let’s change that. Which assumptions does Base make about subtypes of Real? Which methods need to be extended, which are optional, and what rules should the implementation follow?

(This post was inspired by an ongoing discussion over on GitHub.)

Example: Base expects that float(a) returns a subtype of AbstractFloat for any Real input.

Seems this discussion is happening in a few places (here for Number but I also mention Real)

https://github.com/JuliaLang/julia/issues/31937

We also discussed this on slack a few weeks ago, but it’s lost to the slack hole… maybe @quinnj and @jakobnissen expressed similar sentiments?

2 Likes

Yeah, that’s certainly a related discussion. Number and Real raise the same question regarding their interface.

I would define “interface” a little different though. There are no required methods that need to be implemented. It’s also not about implementing every available method that exists for Float64, for example. I mean all the assumptions that generic functions for Real within Base make about how implementations of Reals behave.

Those are not easy to find. Certainly not by simple reflection. Usually these assumptions show up in bug reports in packages when an implementation breaks them.

My example was attempting to illustrate that there exists a specific set of methods in Base that form a “maximum” Number or Real interface that the optional Number methods you want to have documented are drawn from. I want those details documented too, but simple lists of methods would be a good place to start that, no? The assumptions you are talking about are embedded in what those methods actually do. Your example float is one of those methods.

We could probably add some detail here, too:

help?> Number
search: Number LineNumberNode VersionNumber

  Number

  Abstract supertype for all number types.

help?> Real
search: Real real realpath isreal readlink readline readlines areaplot areaplot! CuStreamLegacy ReadOnlyMemoryError

  Real <: Number

  Abstract supertype for all real numbers.
1 Like

Ok, yes. Of course, Base methods can only rely on functions which are already defined in Base. Point taken

It turns out float is a very nice example because it actually doesn’t have a generic method for Real – it only has a fallback method for Any and more specialized types

julia> methods(float,  Base)
# 20 methods for generic function "float":
[1] float(::Missing) in Base at missing.jl:100
[2] float(::Type{T}) where T<:AbstractFloat in Base at float.jl:295
[3] float(::Type{Complex{T}}) where T<:AbstractFloat in Base at complex.jl:49
[4] float(::Type{Complex{T}}) where T in Base at complex.jl:50
[5] float(::Type{Rational{T}}) where T<:Integer in Base at rational.jl:465
[6] float(::Type{#s662} where #s662<:AbstractIrrational) in Base at irrationals.jl:58
[7] float(::Type{T}) where T<:Number in Base at float.jl:294
[8] float(r::StepRange) in Base at float.jl:905
[9] float(r::UnitRange) in Base at float.jl:906
[10] float(r::StepRangeLen{T,R,S} where S where R) where T in Base at float.jl:907
[11] float(r::LinRange) in Base at float.jl:910
[12] float(A::AbstractArray{#s69,N} where N where #s69<:AbstractFloat) in Base at float.jl:896
[13] float(A::AbstractArray{Missing,N} where N) in Base at missing.jl:181
[14] float(A::AbstractArray{Union{Missing, T},N} where N) where T in Base at missing.jl:178
[15] float(A::AbstractArray{T,N} where N) where T in Base at float.jl:899
[16] float(x::Base.TwicePrecision{#s69} where #s69<:AbstractFloat) in Base at twiceprecision.jl:253
[17] float(x::Base.TwicePrecision) in Base at twiceprecision.jl:254
[18] float(z::Complex{#s69} where #s69<:AbstractFloat) in Base at complex.jl:1015
[19] float(z::Complex) in Base at complex.jl:1016
[20] float(x) in Base at float.jl:277