3
votes

I'm new to Julia and it's an exciting language. I just come across some weird behavior that I cannot find an explanation online. I appreciate your help.

    versioninfo()
Julia Version 0.4.0

Commit 0ff703b* (2015-10-08 06:20 UTC)
Platform Info:
  System: Darwin (x86_64-apple-darwin13.4.0)
  CPU: Intel(R) Core(TM) i5-4258U CPU @ 2.40GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.3

I defined a structure

type mytype
    x :: UInt8
    y :: UInt8
    mytype(n::UInt8) = new(n, 0)
end

The type contains a constructor that has a default value for one field and takes input for the other. Then I test it

 mytype(1)

LoadError: MethodError: `convert` has no method matching convert(::Type{mytype}, ::Int64)
This may have arisen from a call to the constructor mytype(...),
since type constructors fall back to convert methods.
Closest candidates are:
  call{T}(::Type{T}, ::Any)
  convert{T}(::Type{T}, !Matched::T)
  mytype(!Matched::UInt8)
while loading In[56], in expression starting on line 1

 in call at essentials.jl:56

The error message is very confusing and I can't understand it. I tested that if I provide default values to the two parameters or take the two inputs, the code works well.

Thanks.

2

2 Answers

3
votes

I get

julia> mytype(1)
ERROR: MethodError: `convert` has no method matching convert(::Type{mytype}, ::Int64)
This may have arisen from a call to the constructor mytype(...),
since type constructors fall back to convert methods.
Closest candidates are:
  call{T}(::Type{T}, ::Any)
  convert{T}(::Type{T}, ::T)
  mytype(::UInt8)
 in call at essentials.jl:56

To break it down, it says it tried to call the constructor, but no constructor matched. Why did no constructor match? Because there is no automatic conversion defined from Int64 (the 1) to UInt8.

It then tried converting a Int64 into mytype, but that method isn't defined either. It then gave up, and displayed the closest things to the thing it originally tried (mytype(::Int64)).

You might want to provide additional methods to handle this, or accept any integer in your constructor and use convert to force it to UInt8 - if it fails, it'll throw an exception:

julia> type mynewtype
           x :: UInt8
           y :: UInt8
           mynewtype(n::Integer) = new(n, 0)
       end

julia> mynewtype(5)
mynewtype(0x05,0x00)
0
votes

In the original question,mytype 's member y will always be 0 because the type inner constructor is called with the second argument 0. They way to construct an instance in this case is : mytype(UInt8(25)) which yields mytype2(0x19,0x00). However, it cannot accept values for y: mytype(UInt8(5), UInt8(23)) yields an error.

In the answer, mynewtype can handle normal Int64 through the inner constructor however, it will always initialize the y with 0 as well ... In Julia, the inner constructor initializes the members of the type.The way to properly handle different argument signatures is by defining the type: type mytype2 x::UInt8 y::UInt8 end and an outer constructor: mytype2(n::Integer) = mytype2(n,0) which calls the default, implicit (and invisible), inner constructor mytype2(x,y) = new(x,y).

mytype2 can be verified by mytype2(1,2) which yields mytype2(0x01,0x02) and, mytype2(1) which yields mytype2(0x01,0x00).

More info at : Julia 0.4 constructors documentation page, "Inner Constructor Methods" (recommendations for safe type definitions at the end of the section)