Wrap and inherit Number



No no, I understand. But I had hoped that if I wanted to mimic, say, Float64, then there would have been an easy way to do that. Kind of like, in stead of starting from scratch: “here is a new type, you don’t know anything about it, let me tell you all you need to know about it”, starting from the top and “adjusting down”: “here is a new type, it’s exactly like Float64, except in this and this and that case”…


You could do something along the lines of

module PseudoNumbers

export PseudoNumber, getnumber  # this is the interface

abstract type PseudoNumber end

function getnumber end

import Base: +                  # and the rest ...

# should define for univariate and multivariate operators using macros,
# this is just an example
+(a::T, b::T) where {T <: PseudoNumber} = T(getnumber(a) + getnumber(b))


and then all the code you would need to write is

using PseudoNumbers

struct MyFloat{T} <: PseudoNumber

PseudoNumbers.getnumber(mf::MyFloat) = mf.x

MyFloat(1.0) + MyFloat(2.0) # works out of the box

but I expect you would have to think about corner cases (lifting reals, how to combine two different types, etc).


One option that we discussed a while back (https://github.com/JuliaLang/julia/pull/3292) is for a package to define a @delegate macro, so that you could do e.g.:

struct A{T<:Number}
@delegate A.x Number

and it would use methodswith(Number, Base) to find all of the (exported) ::Number methods and define corresponding a::A methods that pass through a.x.

I don’t think anyone ever actually tried implementing such a thing, but it would be an interesting experiment.

(TypedDelegation.jl might be a reasonable place to work on this.)


I think that would be useful. Before you suggest I do that, I’ll just add: I suck at meta programming…


While you can implement all the methods that a normal number has for your custom type, there is one thing where it gets tricky: do you want to inherit from Number or not? If you don’t, you can’t pass instances of your type to functions that only accept Numbers, even if your type behaves like a number. I don’t think there is a good solution to this right now, we’ll probably have to wait for either traits or interfaces in some future julia version for an elegant solution.


automatic promotion?


Automatic promotion should be avoided for custom types. They are good only for built-in types.


A few weeks ago @Per shared the intention to work on a library that may suit your needs, if your type will be a subtype of AbstractFloat (but it may not be the case):


Ref this issue https://github.com/JuliaLang/julia/issues/9821.


Sounds great. I’m working on the tests now, but this whole thing is just for my idea with the Angle type. I defined all the functions needed for the conversions and the trigonometry, but now “all” I need is for this Angle type to behave like a Number (or AbstractFloat) in every other situation…


That’s totally wrong. The whole point of Julia’s promotion mechanism is that it is not just for “built-in” types, and is equally extensible to user-defined types. https://docs.julialang.org/en/latest/manual/conversion-and-promotion/

The real problem is that promotion rules are defined for mixed operations on different types, e.g. if you do a + b where a and b are different Number types then it will call promote(a,b) by default. This doesn’t help you at all for exp(x), for example, or for non-Number types, where the promotion code is not called in the default methods.


The @forward macro from Lazy.jl makes this a bit easier by allowing you to delegate a given list of functions to a specific field of a type: https://github.com/MikeInnes/Lazy.jl/blob/master/src/macros.jl#L267

(this is not nearly as fancy as the hypothetical @delegate, but at least it exists right now)


DataStructures.jl has an implementation of @delegate. If there’s interest, that could be turned into a (1 or 2 macro) package.



To be clear, that implementation of delegate is not as full featured as @stevengj’s described version above.


Very cool.

I’d like to add some clarity as well:
The “thing” that would be most useful (and maybe I should open a new topic for this), is if we could just have something that acts exactly like a Number (in every meaning), but that we can override some of the functions that already accept it as an argument.

So here’s an instance of Float64:
We can do all sorts of things with it. Here is an instance of a custom type A:
We can do exactly the same things like the Float64 version from before. Now I change the existing sin function to the following (silly):
sin(x::A) = 2x.
There. Instances of A do everything, say, floats can do, and I modified the behavior of a few select functions. Winning.

Angle package

How would you address https://github.com/JuliaLang/julia/issues/9821#issuecomment-70381499?


I liked the idea of TypedDelegation.jl very much, will these macros be in Base by any chance at some point?


Almost certainly not, since they are unlikely to be needed for anything in Base.


Thank you @stevengj, good to know.


It’s not totally true, see e.g length, size etc. for Generator in generator.jl. Some time ago I started a branch with this delegate functionality, but thought I indeed needed to find more cases to push that into Base! In my case, I needed indirection for the iteration protocol (similar to the iter function in python).