@jameson Have you bothered to benchmark this, or look at the generated code?
I have, and it bears out what I’ve been trying to get across to you.
Before making inflammatory statements (i.e. “sheer and utter nonsense”), you should check out the facts first.
Also, you seem to have a misunderstanding as to what is going on when types are created.
Code definitively is run when a type is created, and you can use functions that return the types used for the fields.
The issue is that those functions are run when the type is created, which doesn’t work if the calculations contain any type parameters, since those are not yet bound.
That is where Jeff agreed (when asked both at JuliaCon 2016 and JuliaCon 2017, @andyferris is a witness!) that that could be fixed (as I had already pointed out) by created a thunk if there were any type parameters used in the expression that would get evaluated when a concrete type was instantiated from that parameterized type, i.e. when the parameters were all bound)
The workaround of doing the calculations when an object is created, instead of fixing the bug of prematurely evaluating the type variables, is simply too slow for many of the cases where this would be useful (such as for new faster numeric types, where you want to create a struct (immutable) with a number of limbs that depends on the number of bits of precision desired and the machine integer size).
I’m talking about when parameterized types are created, when concrete types are actually instantiated from those parameterized types, when values are constructed, and when potentially costly calculations are done, i.e. either at the time that the concretes types are instantiated, or when every value is constructed (as happens now with the workaround).
As far as I understand (from inspecting the code, and inserting debugging statements into the code called when types are created), what currently happens is:
- when a parameterized type is created, any expressions to calculate the types of the fields are executed. If a function is called with a type parameter, it is passed something of type TypeVar, which indicates that it is bound or unbound.
This is used for example with the Maybe(T)
function, which returns a Union{T, Void}
, which I’ve seen in various places in Julia code.
This type is cached by Julia.
- when a concrete type is created from a parameterized type, any fields whose type was a type parameter, or a type parameterized by the type parameters, have those type vars replaced by their bound values.
This concrete type is then also cached by Julia.
What needs to happen is:
- when a parameterized type is created, thunks are created for any expression that uses a type parameter in the body of the type definition. Otherwise, expressions without type variables can be immediately evaluated, as occurs now.
- when a concrete type is created from a parameterized type, any thunks are evaluated with the type vars replaced by their bound values.
This would eliminate the need for the “hidden” parameters needed by the current workaround, which can make the types harder to understand, and expose internal implementation details.