Setting the fix-size mutable array thing aside, one thing that's glaringly
wrong about these examples is that even if parametric types were covariant,
this wouldn't make sense because 4 <: Any is never going to be true because
4 is not a type, it's a value:
julia> 4 <: Any
ERROR: TypeError: subtype: expected Type{T}, got Int64
in eval at ./boot.jl:265
The fact that you can write NTuple{Any,Int} at all seems to be a bug:
julia> NTuple{Any,Int}
ERROR: TypeError: NTuple: in parameter 1, expected Int64, got Type{Any}
in eval at ./boot.jl:265
julia> NTuple{ANY,Int}
NTuple{ANY,Int64}
I'm not sure what's going on with that, but I've filed an issue: #14607
<https://github.com/JuliaLang/julia/issues/14607>.
On Fri, Jan 8, 2016 at 2:00 PM, 'Jamie Brandon' via julia-users <
[email protected]> wrote:
> > If you don't want to specialize on the length of the array why include
> it in the type at all?
>
> I built a HAMT using normal Julia arrays and found that the extra size
> and extra pointer hop made them around 2x larger and 4x slower than
> the totally naive Rust equivalent. (If you want excruciating amounts
> of detail, see https://github.com/jamii/imp/blob/master/diary.md#baseline
> ).
> I'm now trying to implement fixed-length mutable arrays, much like
> https://github.com/JuliaLang/julia/issues/12447
>
> type NArray{N,T}
> contents::NTuple{N,T}
> end
>
> If I take the size out it will just box the ntuple.
>
> I can work around the constructor. I'm more interested in
> understanding how ANY affects variance so I know in what cases I can
> use it:
>
> julia> NTuple{4, Int64} <: NTuple{ANY, Int64}
> true
> julia> Hamt.NArray{4,Int64} <: Hamt.NArray{ANY,Int64}
> true
>
> julia> Type{NTuple{4,Int64}} <: Type{NTuple{ANY,Int64}}
> true
> julia> Type{Hamt.NArray{4,Int64}} <: Type{Hamt.NArray{ANY,Int64}}
> false
>
> julia> Tuple{NTuple{4,Int64}} <: Tuple{NTuple{ANY,Int64}}
> true
> julia> Tuple{Hamt.NArray{4,Int64}} <: Tuple{Hamt.NArray{ANY,Int64}}
> true
>
> julia> Vector{NTuple{4,Int64}} <: Vector{NTuple{ANY,Int64}}
> true
> julia> Vector{Hamt.NArray{4,Int64}} <: Vector{Hamt.NArray{ANY,Int64}}
> false
>
> >> julia> arr = Hamt.NArray{10, Int64}()
> >> ERROR: MethodError: `convert` has no method matching
> >> convert(::Type{Hamt.NArray{10,Int64}})
> >> This may have arisen from a call to the constructor
> >> Hamt.NArray{10,Int64}(...),
> >> since type constructors fall back to convert methods.
> >> Closest candidates are:
> >> convert{T}(::Type{T}, ::T)
> >> Hamt.NArray{N,T}(, ::Any)
> >> call{T}(::Type{T}, ::Any)
> >> in call at essentials.jl:57
> >>
> >> julia> arr = Hamt.Array{Int64}()
> >> ERROR: argument is an abstract type; size is indeterminate
> >> in call at /home/jamie/code/imp/src/Hamt.jl:23
> >>
> >> I have this doing exactly what I want with the bare types eg:
> >>
> >> Base.getindex{T}(narray::NArray{ANY,T}, ix::Integer)
> >>
> >> I'm just struggling getting the same behaviour from the constructor
> >> because of the way Type varies.
> >
> >
> > So one issue here is that using ANY like this doesn't mean what you want
> it
> > to – it means that the first parameter of NArray is the literal value
> ANY.
> > So when you write call{T}(t::Type{Array{T}}) it literally means
> > call{T}(t::Type{NArray{ANY,T}}) – which is not what you want. Instead,
> you'd
> > want a typealias like this:
> >
> > typealias TArray{T,n} NArray{n,T}
> >
> > call{T}(t::Type{TArray{T}}) = ...
> >
> >
> > But I'm getting a little confused about what you're trying to accomplish
> > with that type parameter for the number of elements in the first place.
>
> On 8 January 2016 at 18:23, Stefan Karpinski <[email protected]> wrote:
> > On Fri, Jan 8, 2016 at 1:12 PM, 'Jamie Brandon' via julia-users
> > <[email protected]> wrote:
> >>
> >> > Yes, it's like any other parametric type that way.
> >>
> >> Are tuples treated specially? eg:
> >
> >
> > Yes, tuples types are covariant while everything else is invariant.
> >
> >> > ANY is a hack to let you hint to the compiler that it should not
> >> > specialize a method on an argument.
> >>
> >> That's exactly what I'm trying to achieve. I really don't want my
> >> array functions to specialize on the length of the array :)
> >
> >
> > If you don't want to specialize on the length of the array why include
> it in
> > the type at all?
> >
> >>
> >> > Currently you have to use a typealias...
> >>
> >> I'm not having any luck with this.
> >>
> >> typealias Array{T} NArray{ANY,T}
> >>
> >> call{T}(t::Type{Array{T}}) = begin
> >> tp = pointer_from_objref(t)
> >> size = sizeof(t)
> >> ...
> >> end
> >>
> >> julia> arr = Hamt.NArray{10, Int64}()
> >> ERROR: MethodError: `convert` has no method matching
> >> convert(::Type{Hamt.NArray{10,Int64}})
> >> This may have arisen from a call to the constructor
> >> Hamt.NArray{10,Int64}(...),
> >> since type constructors fall back to convert methods.
> >> Closest candidates are:
> >> convert{T}(::Type{T}, ::T)
> >> Hamt.NArray{N,T}(, ::Any)
> >> call{T}(::Type{T}, ::Any)
> >> in call at essentials.jl:57
> >>
> >> julia> arr = Hamt.Array{Int64}()
> >> ERROR: argument is an abstract type; size is indeterminate
> >> in call at /home/jamie/code/imp/src/Hamt.jl:23
> >>
> >> I have this doing exactly what I want with the bare types eg:
> >>
> >> Base.getindex{T}(narray::NArray{ANY,T}, ix::Integer)
> >>
> >> I'm just struggling getting the same behaviour from the constructor
> >> because of the way Type varies.
> >
> >
> > So one issue here is that using ANY like this doesn't mean what you want
> it
> > to – it means that the first parameter of NArray is the literal value
> ANY.
> > So when you write call{T}(t::Type{Array{T}}) it literally means
> > call{T}(t::Type{NArray{ANY,T}}) – which is not what you want. Instead,
> you'd
> > want a typealias like this:
> >
> > typealias TArray{T,n} NArray{n,T}
> >
> > call{T}(t::Type{TArray{T}}) = ...
> >
> >
> > But I'm getting a little confused about what you're trying to accomplish
> > with that type parameter for the number of elements in the first place.
>