user42961
user42961

Reputation: 93

Julia methoderror for eltype style functionality

I have an abstract container AbstractContainer parameterised over a type T which indicates the type of what is in the container. Every subtype (in this case FloatContainer) then specifies what's actually in the container (in this case a Float64).

Ideally I'd have a means of getting back what type is in the container if I only have the container type. This way I could use it in another struct (in this example MultiplyBy)

I was thinking of doing it in a similar way to Julia's internal eltype function but I can't get it to work. I always get a method error (see the last snippet for the detailed error message)

abstract type AbstractContainer{T} end


gettype(::Type{AbstractContainer{T}}) where T = T

struct FloatContainer <: AbstractContainer{Float64}
  x::Float64
end


struct MultiplyBy{T<:AbstractContainer}
  x::gettype(T)
end


function process(m::MultiplyBy, v::AbstractContainer)
  return typeof(v)(m.x*v.x)
end


function main()
  x = FloatContainer(2.0)

  y = FloatContainer(3.0)
  op = MultiplyBy{FloatContainer}(y)

  z = process(op, x)
  println(z.x)
end


main()
ERROR: LoadError: MethodError: no method matching gettype(::TypeVar)
Closest candidates are:
  gettype(!Matched::Type{AbstractContainer{T}}) where T at /Users/.../test.jl:6

I must admit I'm very new to Julia but I'm very interested in learning more about it. So any tips are appreciated - either on how to solve this problem differently or where my mistake is.

Upvotes: 3

Views: 150

Answers (2)

David Sainez
David Sainez

Reputation: 6976

Determining element type

Your gettype does not work because it dispatches on abstract types, but your container objects will all have concrete types. You have to use the subtype operator in order to dispatch correctly.

Compare dispatch on abstract types:

julia> eltype1(::Type{AbstractContainer{T}}) where T = T
eltype1 (generic function with 1 method)

julia> eltype1(FloatContainer)
ERROR: MethodError: no method matching eltype1(::Type{FloatContainer})
Closest candidates are:
  eltype1(::Type{AbstractContainer{T}}) where T at REPL[4]:1
Stacktrace:
 [1] top-level scope at REPL[5]:1

julia> eltype1(AbstractContainer{Float64})
Float64

with dispatch on subtypes of abstract types:

julia> eltype2(::Type{<:AbstractContainer{T}}) where T = T
eltype2 (generic function with 1 method)

julia> eltype2(FloatContainer)
Float64

julia> eltype2(AbstractContainer{Float64})
Float64

Prefer dispatch variables to explicit eltype calls

Calling eltype directly is usually unnecessary; you can make the parametric type explicit during dispatch.

This solution uses only parametric types:

julia> struct Container{T}
           x::T
       end

julia> struct MultiplyBy{T}
           x::T
       end

julia> MulitplyBy(x::Container{T}) where T = MultiplyBy{T}(x.x)
MulitplyBy (generic function with 1 method)

julia> process(m::MultiplyBy, x::Container{T}) where T = Container{T}(m.x * x.x)
process (generic function with 1 method)

julia> a = Container(2.0)
Container{Float64}(2.0)

julia> b = Container(3.0)
Container{Float64}(3.0)

julia> op = MulitplyBy(b)
MultiplyBy{Float64}(3.0)

julia> process(op, a)
Container{Float64}(6.0)

Upvotes: 3

longemen3000
longemen3000

Reputation: 1313

i'm not sure about what you need, but i coded an working example with your structure:

abstract type AbstractContainer{T} end

#not necessary anymore
#gettype(a::Type{AbstractContainer{T}})  where T = T

struct FloatContainer{T<:AbstractFloat} <: AbstractContainer{T} #AbstractFloat, to include Float32, BigFloats and others
  x::T
end

 #you can't use functions on struct definitions, if you need more restrictions,
 #use a Constructor
struct MultiplyBy{T<:AbstractContainer}
  x::T
end

#example external Constructor

MultiplyBy(x::AbstractFloat) = MultiplyBy(FloatContainer(x))

value(x::AbstractContainer) = x.x #basic accesor function
value(x::MultiplyBy) = value(x.x) 

function process(m::MultiplyBy, v::AbstractContainer)
  return typeof(v)(value(m)*value(v)) #calling accesor functions to access the values.
end

function main()
  x = FloatContainer(2.0)

  y = FloatContainer(3.0)
  op = MultiplyBy(y) #the parametric type is inferred automatically, no need to use Curly Braces

  z = process(op, x)
  println(value(x)) #better use accesor functions that direct access, as the underlying implementation can change
end

main()

Upvotes: 0

Related Questions