Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 327
StephUnna Avatar asked Jan 21 '15 14:01

StephUnna


1 Answers

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).

like image 95
mbauman Avatar answered Nov 05 '22 03:11

mbauman