I’m trying to create a type with an @generated inner constructor, but it seems I’m doing something wrong? Or maybe generated inner constructors are not permitted?
Here’s my attempt:
julia> immutable T
@generated T() = :( new() )
end
julia> T()
ERROR: UndefVarError: new not defined
Stacktrace:
[1] T() at ./REPL[10]:2
I don’t think it’s currently easy to do. You can generate the Expr(:new) manually but maybe we can fix the generated function lowering for inner constructors…
Expr(:new) gave a segfault when I tried it. Expr(:call, :new) gave the same error message as :( new() ).
The reason I would like a generated inner constructor is that I want to do some sanity checks on type parameters, and throw an exception if there’s a problem. I had expected to be able to do this without any performance penalty, since type parameters are known constants when the method is compiled. But it turns out to be more complicated than I first thought. Instructions related to garbage collection sometimes get added even though they are not needed. (Apparently the compiler does not always realize when the garbage collected objects are constants that get folded away.) A generated function would not have this problem.
I had tried this, but it did not work when I passed the type parameters as arguments to the checking function. (Apparently the types themselves become garbage collected objects at that point.) The example you give instead infers the type parameters anew from the the types of passed-through arguments. I can see why that should work much better. I will try this!
It should also work when passing types to the checking function. The key is to put your logic into the dispatch table instead of inside a single method. Dispatching on ::Type{…} isn’t really any different than dispatching on concrete types… although invariance can make it a little harder to express.
I put the logic in a @generated checking function. Equivalent to putting it in the dispatch table in a way, but gives more flexibility. Works great! Thanks for the tip.
which suggested I might try this, but it doesn’t work:
julia> struct Foo
@generated Foo() = :(new(Foo))
end
julia> Foo()
ERROR: UndefVarError: new not defined
Stacktrace:
[1] macro expansion at ./REPL[6]:2 [inlined]
[2] Foo() at ./REPL[6]:2
[3] top-level scope at REPL[7]:1
FWIW my use case is that I have a complicated type with lots of parameters and ways to construct it, and I think it would be far clearer code to deal with ambiguities in the body of a generated function rather than with dispatch logic.