What on earth is an inner constructor?

By the way of example, suppose you want to define a type to represent even numbers:

julia> struct Even
          e::Int
       end

julia> Even(2)
Even(2)

So far so good, but you also want the constructor to reject odd numbers and so far Even(x) does not:

julia> Even(3)
Even(3)

So you attempt to write your own constructor as

julia> Even(x) = iseven(x) ? Even(x) : throw(ArgumentError("x=$x is odd"))
Even

and … drum roll, please … It does not work:

julia> Even(3)
Even(3)

Why? Let’s ask Julia what she has just called:

julia> @which Even(3)
Even(e::Int64) in Main at REPL[1]:2

This is not the method that you defined (look at the argument name and the type), this is the implicitly provided constructor. Maybe we should redefine that? Well, don’t try this at home:

julia> Even(e::Int) = iseven(e) ? Even(e) : throw(ArgumentError("e=$e is odd"))
Even
julia> Even(2)
ERROR: StackOverflowError:
Stacktrace:
 [1] Even(::Int64) at ./REPL[11]:0
 [2] Even(::Int64) at ./REPL[11]:1 (repeats 65497 times) 

We’ve just created an infinite loop: we redefined Even(e) to recursively call itself. We are now facing a chicken and egg problem: we want to redefine the implicit constructor, but we need some other constructor to call in the defined function. As we’ve seen calling Even(e) is not a viable option.

The solution is to define an inner constructor:

julia> struct Even
          e::Int
          Even(e::Int) = iseven(e) ? new(e) : throw(ArgumentError("e=$e is odd"))
       end


julia> Even(2)
Even(2)

julia> Even(3)
ERROR: ArgumentError: e=3 is odd
..

Inside an inner constructor, you can call the original implicit constructor using the new() syntax. This syntax is not available to the outer constructors. If you try to use it, you’ll get an error:

julia> Even() = new(2)
Even

julia> Even()
ERROR: UndefVarError: new not defined 
..

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)