15

The Julia style guide says the following:

Don’t use unnecessary static parameters. A function signature:

foo{T<:Real}(x::T) = ...

should be written as:

foo(x::Real) = ...

I expected that to apply to array parameters too. However, if I write the following code:

ret1(x::Real) = x
ret2(x::Array{Float64,2}) = x
ret3(x::Array{Real,2}) = x
ret1(1.0)
ret2(rand(2,2))
ret3(rand(2,2))

then I get the following console output (using Julia 0.2.1):

MethodError(ret3,(
2x2 Array{Float64,2}:
 0.841121  0.322133
 0.469432  0.495438,))

So why does Julia throw an error for arrays with abstract type parameters, but not for variables with abstract types?

3 Answers 3

10

In the case of ret3, the type parameter is actually necessary because an Array{Real} is a type that can never be constructed Julia's type parameters are invariant. This is a bit of a subtle topic but the key fact is that while Float64 <: Real is true, Array{Float64} <: Array{Real} is not. This is a bit confusing at first, but this is necessary for the compiler to be able to know the memory layout of function arguments when doing code generation. See the manual for more on this.

So you can dispatch on a Real as in ret1 because when you pass it a Float64, Float64 <: Real is true, whereas in ret3 you are passing it a Array{Float64}, and Array{Float64} <: Array{Real} is not the case, hence the no method error. To fix this, use a type parameter:

julia> ret3{T <: Real}(x::Array{T,2}) = x
ret3 (generic function with 2 methods)

julia> ret3(rand(2,2))
2x2 Array{Float64,2}:
 0.0857132  0.353194
 0.802737   0.717292
Sign up to request clarification or add additional context in comments.

6 Comments

Yes, I thought that would be the answer. But then it requires static parameters which would otherwise seem unnecessary. Why doesn't Julia have an array type which enforces all elements being the same type?
I'm somewhat confused, Julia an infinite number of array types that enforce all elements being the same type: Array{Float64}, Array{String}, etc., etc. Does you mean something that enforces elements being of the same type without having to specify what the type is?
What I'm suggesting is an array type such as ArrayHomogenous{T} where T can be an abstract type such as Real or Number, but for which the compiler knows that every element will be the same type, whatever that type may be.
That's a good question, but my guess is because it isn't really that useful in practice — you almost always pass homogenous arrays of a concrete type as arguments anyway and the compiler is able to specialize on that. Having an array whose elements are all guaranteed to be some subtype of Real is not particularly useful without specifying which subtype since without that information almost no structural information is being provided to the compiler (e.g. about memory layout, etc.).
I'd make one very small change to James' answer: Array{Real} can be constructed, which is why x::Array{Real} is taken to refer to a unique concrete type, but Real cannot be constructed, so x::Real can be safely taken to refer to a family of many concrete types.
|
2

In the case of ret3, a strictly Array{Real, 2} is expected, i.e., an array which can hold any kind of Real variables inside (while rand(2,2) is an array of Float64 only).

In this case the static parameter is not unnecessary:

ret3{T<:Real}(x::Array{T,2}) = x

Comments

0

As of julia 1.2, I believe the appropriate typing for ret3 is:

ret3(x::Array{<:Real,2})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.