Fernando
Fernando

Reputation: 655

Inner constructor in Julia with different name than the struct

I saw this construction in rationals.jl, and I am trying to understand it. I can write

struct Bar
    y
    global func(x) = new(x)
end

Then if I try:

a = Bar()

I get an error. It is like func is now an inner constructor. And I can actually use it by writing:

a = func(2)

If I remove the keyword global, I can't use this inner constructor anymore. What is going on? Why is global there, and could I use the inner constructor somehow without it (perhaps by qualifying the name)?

How can the inner constructor have a different name than the struct itself? What, in fact, is an inner constructor?

Upvotes: 2

Views: 205

Answers (1)

phipsgabler
phipsgabler

Reputation: 20970

TL/DR

An inner constructor is a method of the struct type (i.e., (::Type{Bar})(...) which has access to new. You can put arbitrary method definitions inside a struct -- there is no technical requirement to only have inner constructors -- but this is only useful in rare cases.


Long version

This is a rare case that the manual doesn't even cover explicitely (and I long didn't know was legal at all). In my understanding, two rules apply:

  1. Inside the struct definition, and only there, you are allowed to use new.
  2. If any inner constructor method is defined, no default constructor method is provided: it is presumed that you have supplied yourself with all the inner constructors you need.

However, nothing requires the inner constructor to be a method of the struct type! Instead you may actually define arbitrary methods.

And then, scoping applies. With a "regular" inner constructor, what you get is a method for Type{Bar}. This requires no global, as the type is present in outer scope.

In your case, without the global, func is local and so there is no constructor accessible at all outside the struct definition. This makes little sense. The global function definition is something that can be useful in rare cases when you want to prevent direct construction of values of you type, but only allow conversion. I have seen this kind of pattern:

struct Baz
    Base.reinterpret(::Type{Baz}, ...) = new(...)
end

This adds a (globally available) method to reinterpret, the only entry point to constructing Bazes. Putting it inside the struct is necessary, as at some place you need to create the object and thus require access to new.

Upvotes: 4

Related Questions