How should I declare “abstract container type” fields in v0.6?


#1

Hi,

Is there an updated information for how to create an abstract container type in v0.6?

Here’s the corresponding info for 0.4:

How should I declare “abstract container type” fields?

Is it now recommended to inherit from AbstractArray?

Thanks,

Glen


#2

That advice still holds: type-fields used in performance critical parts of a code should have a concrete type. If you want the type-field to hold a range of types you need to parameterize the type.

You can check that yourself (using the linked example):

               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: http://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.0-dev.2826 (2017-02-14 13:01 UTC)
 _/ |\__'_|_|_|\__'_|  |  Commit 2ecc2157c5* (12 days old master)
|__/                   |  x86_64-pc-linux-gnu

julia> type MySimpleContainer{A<:AbstractVector}
            a::A
       end

julia> type MyAmbiguousContainer{T}
            a::AbstractVector{T}
       end


julia> f(v) = v.a
f (generic function with 1 method)

julia> @code_warntype f(MySimpleContainer([1]))
Variables:
  #self#::#f
  v::MySimpleContainer{Array{Int64,1}}

Body:
  begin
      return (Core.getfield)(v::MySimpleContainer{Array{Int64,1}}, :a)::Array{Int64,1}
  end::Array{Int64,1} # good

julia> @code_warntype f(MyAmbiguousContainer([1]))
Variables:
  #self#::#f
  v::MyAmbiguousContainer{Int64}

Body:
  begin
      return (Core.getfield)(v::MyAmbiguousContainer{Int64}, :a)::AbstractArray{Int64,1}
  end::AbstractArray{Int64,1} # bad

#3

That is a very helpful example, thanks. Would you not inherit from AbstractArray to get a bunch of functionality? I thought that was improved in v0.6.

Is there a good way to restrict the element type of v.a to be a subtype of Number and be performant? Similarly, if I try to restrict the eltype with a function call I run into problems:

julia> g{A<:AbstractVector,N<:Number}(m::MySimpleContainer{A{N}}) = "yes"
ERROR: TypeError: Type{...} expression: expected UnionAll, got TypeVar

julia> g(m::MySimpleContainer{A{N}}) where N<:Number where A<:AbstractVector = "yes"
ERROR: TypeError: Type{...} expression: expected UnionAll, got TypeVar

julia> g{A<:AbstractVector}(m::MySimpleContainer{A{<:Number}}) = "yes"
ERROR: TypeError: Type{...} expression: expected UnionAll, got TypeVar

julia> g{A<:AbstractVector}(m::MySimpleContainer{A}) where N<:Number where A <: AbstractVector{N}= "yes"
ERROR: UndefVarError: N not defined

julia> g{A<:AbstractVector}(m::MySimpleContainer{A}) where A<:AbstractVector{N} where N<:Number = "yes"
ERROR: TypeError: Type{...} expression: expected UnionAll, got #g

After reading the manual, I’m not sure how to use the where clause effectively.

Glen


#4

Yes, but that is in 0.5 already (but maybe it improved). (But that is a different topic to the section of the manual you link to). See http://docs.julialang.org/en/stable/manual/interfaces/ for the bits you need to implement additionally.

type MySimpleContainer{A<:Number}
            a::A
       end

I struggle too :slight_smile: . This seems ok:

g(m::MySimpleContainer{A}) where A<:AbstractVector{N} where N<:Number = "yes"

#5

The type definition and inheritance make things more complicated. I’d like to define a type that has the same constraints as:

g(m::MySimpleContainer{A}) where A<:AbstractVector{N} where N<:Number = "yes"

I’d like to define a container type that inherits from AbstractVector and is limited to Numbers. Like:

struct MySimpleContainer{A} where A<:AbstractVector{N} where N<:Number <: AbstractVector{N}
    data::A
end

But where clauses are not allowed in type definitions so I’m not sure what is the best way to do this.

Thanks,

Glen


#6

You mean

julia> struct MySimpleContainer{N<:Number,A<:AbstractVector{N}} <: AbstractVector{N}
       end

?


#7

Hi yuyichao,

Thanks for the help. This is pretty exciting as it now works and should be performant:

julia> struct MySimpleContainer{N<:Number,A<:AbstractVector{N}} <: AbstractVector{N}
         data::A
       end

julia> Base.size(m::MySimpleContainer) = size(m.data)

julia> Base.getindex(m::MySimpleContainer, idx) = getindex(m.data, idx)

julia> MySimpleContainer(data::AbstractVector) = MySimpleContainer{eltype(data),typeof(data)}(data)
MySimpleContainer

julia> MySimpleContainer([1,2,3])
3-element MySimpleContainer{Int64,Array{Int64,1}}:
 1
 2
 3

julia> f(x) = x.data
f (generic function with 1 method)

julia> @code_warntype f(MySimpleContainer([1,2,3]))
Variables:
  #self#::#f
  x::MySimpleContainer{Int64,Array{Int64,1}}

Body:
  begin
      return (Core.getfield)(x::MySimpleContainer{Int64,Array{Int64,1}}, :data)::Array{Int64,1}
  end::Array{Int64,1}

julia> typeof(MySimpleContainer([1,2,3]))
MySimpleContainer{Int64,Array{Int64,1}}

I wasn’t sure the best way to do the outer constructor but it seems to work.

Is there any way to get a type of the container to be MySimpleContainer{Array{Int64,1}} so Int64 isn’t there twice (it seems kind of redundant).

Glen


#8

No, that is not possible. Related issue: https://github.com/JuliaLang/julia/issues/18466