How can I specify a type for a function argument without restricting its dimensions?
Asked Answered
R

1

5

In Julia, I want to specify the type of a function argument as an array of arrays. So I have

function foo{T <: Any}(x::Array{Array{T}})

but if I set the argument x in the REPL, for example:

x = Array[[0,1],[1,2,3],[0,1,2,4]]

then it automatically gets the following type assignment (for example), which includes its dimensions:

x::Array{Array{T,N},1}

so that I get the error

ERROR: `foo` has no method matching foo(::Array{Array{T,N},1}).

I don't want to restrict the array dimensions at all, so was thinking that the solution maybe something along the lines of

function foo{T <: Any, N <: Number}(x::Array{Array{T,N},N})

but this doesn't work either.

How can I specify the argument type to be an array of arrays?

Rame answered 21/1, 2015 at 14:40 Comment(2)
Could you include an example of how to generate something like your x? My hunch is that you're running into invariance, and that a definition like foo{T<:Array}(x::Array{T}) will do the trick.Hospitium
Yep that works, I wasn't sure about the rules for putting the {T<:Array} bit in. For example if I had more than one argument with the same issue, how can I put in multiple type parameters? Thanks for the link, it was very helpful, I wasn't sure where to look before. I have added an example of how I generate x to the question.Rame
H
7

Given an array of arrays x = Array[isodd(i) ? [1i,2i] : [1.0i 2.0i] for i=1:10], Julia reports its type as Array{Array{T,N},1}. This is deceiving, as it seems to imply that there exists some T and some N for which the above type will match. But that's not the case: the odd elements will be of type Array{Int,1}, and the evens are Array{Float64,2}. So when you try to write a method for foo with the type parameters:

foo{T,N}(::Array{Array{T,N},1}) = T,N

What are T and N for x? Clearly, there is no such N — it's both 1 and 2! And the elements of these subarrays aren't of type Any — they're both Int and Float64. The same applies for Array[[0,1],[0,1,2]], even though in your example you know that T and N are consistent, Julia's type system doesn't… and you could potentially push elements that aren't Int vectors.

There are quite a few ways around this. The best approach is to try to make sure that your arrays always have concrete (or at least uniform) element types, but that's not always possible. Given your example x above, you could instead write: x = Array{Int,1}[[0,1],[1,2,3],[0,1,2,4]].

Another alternative is to change your function signature:

foo{N}(x::Array{Array,N}) = 1 # Will *only* work for arrays like x above
foo{T<:Array, N}(x::Array{T,N} = 2 # Will work for all arrays of arrays

The first will only apply if you have exactly that type due to invariance, whereas the second will work for all Arrays of Arrays, both poorly-typed and concrete.

(Edit: As a final note, N<:Number won't match literal numbers. It will match types that are a subtype of Number, like Real or Int. There's currently no way to express that a type parameter must be a value of type Int beyond the convention that N is an integer).

Hospitium answered 21/1, 2015 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.