Julia: clarify inner constructor behavior

Created on 5 Dec 2016  路  6Comments  路  Source: JuliaLang/julia

In the "Inner Constructors" section of the manual, it says:

The default constructor is equivalent to writing your own inner constructor method that takes all of the object鈥檚 fields as parameters (constrained to be of the correct type, if the corresponding field has a type), and passes them to new, returning the resulting object

Which I would read to mean that:

type T1
  x::Int64
end

would be equivalent to:

type T1
  x::Int64
  T1(x::Int64) = new(x)
end

But right below it gives the above equivalent example as:

type T1
  x::Int64
  T1(x) = new(x)
end

Which seems to me to imply that new calls convert on the fields, because non-Int64 arguments are converted to Int64.

In the "Types" section of the manual it says:

Two constructors are generated automatically (these are called default constructors). One accepts any arguments and calls convert() to convert them to the types of the fields, and the other accepts arguments that match the field types exactly.

Which I would read to mean that:

type T1
  x::Int64
end

would be equivalent to:

type T1
  x::Int64
  T1(x::Int64) = new(x)
end

T1(x) = T1(convert(Int64, x))

It seems that these two descriptions of the default constructors (either converting in the outer constructor or converting in new) would behave identically, but it's confusing that it's described both ways in the manual. Is one of them more correct?

Hacktoberfest doc

Most helpful comment

If anyone's feeling extra ambitious, it'd be great to clarify how to use type parameters w/ new in inner constructors. Even as someone who has used julia for years, I have trouble knowing how exactly to express something like:

# type w/ type parameter tag
type A{T}
   val::Int

   A{T}(val) = new(convert(Int, val)) # ?
   A(val) = new{T}(convert(Int, val)) # ?
end

All 6 comments

It actually generates both:

julia> expand(quote
       type T1
         x::Int64
       end
       end)
type T1
  x::Int64
  T1(x::Int64) = new(convert(Int64, x))
  T1(x) = new(convert(Int64, x))
end

It sounds like parts of the manual may be out-of-date?

It seems like from the user's perspective it the T1(x::Int64) definition doesn't matter, right?

Also, if you define:

type T1
  x::Int64
  T1(x) = new(x)
end

Then T1(1.0) still works, so it seems that in that case it's still converting. Looking at new-call in the parser it looks like a convert is automatically inserted even if the user overrides the inner constructor .

So maybe the equivalence in the Constructors section is the best one:

type T1
  x::Int64
  T1(x) = new(x)
end

But removing the comment about restricting argument types, and adding a note that new converts all the field types.

If that seems correct I can edit the docs.

The T1(x::Int64) constructor is sometimes important. Otherwise e.g. T1(x::Real) = T1(round(Int64, x)) would cause stack overflow.

Ah, good point. Thanks!

That trick with expand is very neat!

If anyone's feeling extra ambitious, it'd be great to clarify how to use type parameters w/ new in inner constructors. Even as someone who has used julia for years, I have trouble knowing how exactly to express something like:

# type w/ type parameter tag
type A{T}
   val::Int

   A{T}(val) = new(convert(Int, val)) # ?
   A(val) = new{T}(convert(Int, val)) # ?
end
Was this page helpful?
0 / 5 - 0 ratings

Related issues

arshpreetsingh picture arshpreetsingh  路  3Comments

StefanKarpinski picture StefanKarpinski  路  3Comments

yurivish picture yurivish  路  3Comments

ararslan picture ararslan  路  3Comments

wilburtownsend picture wilburtownsend  路  3Comments