Generating automatic constructors for parametric type

MWE: for a parametric type definition with an inner constructor

struct Foo{T <: Real}
    x::T
    function Foo{T}(x) where T
        @assert x > 0
        new(x)
    end
end

I frequently end up writing boilerplate

Foo(x::T) where T = Foo{T}(x)

Is there a construct in Base which would allow me to get these for free? Or perhaps a macro in a package?

(incidentally, is the above the new v0.6 syntax? I am still confused about which <: becomes a where.)

1 Like

Slightly off topic, but related (somewhat)

I parse these kinds of single line function definitions wrong all the time in my head.
Will the syntax Foo{T}(X::T) = Foo{T}(x) continue to work in 1.0? it seems to still work in 0.6

Foo(x::T) where {T} = Foo{T}(x) looks better.

The exact “boilerplate” constructor you mentioned is provided for free whenever no inner constructor is defined.

AFAICT if you need an inner constructor (for an invariant, say) then it is assumed you are an advanced user doing something fancy, and the default outer constructor could potentially get in your way (remember while you can define a method, you can’t really “undefined” one, so as unfortunate as it is I’m not sure a better choice is available).

@andyferris: I am afraid you misunderstand the question. The issue is not whether one is an “advanced user” who can write this, but that one is repeating code unnecessarily.

It is easy to write a macro for this, but the problem is that AFAICT those kinds of macros (eg Parameters.@with_kw, @auto_hash_equals) cannot be combined. Maybe a meta-macro

@struct_meta{with_kw, auto_has_equals, parametric_constructor} struct Foo
    ...
end

could be the solution, where each expander gets the form struct Foo end and spits out extra code which is then combined.

Using QuickTypes.jl

using QuickTypes

@qstruct Foo{T <: Real}(x::T) do
    @assert x > 0
end

It defines both the inner and outer constructors.

1 Like

Was there ever a good reply for why defining an inner constructor removes the automatic nonparametrized “boilerplate” methods?

That allows one to create a type and determine all behaviors. If the default methods were made to exist (invisibly) alongside the specialty type, unexpected interactions can (and do) occur.