DFL
DFL

Reputation: 125

What does "where T" in julia do without any specification after T?

I'm new to julia and I have not been able to find an explanation of where that fully makes sense to me. So I understand that for example function f(x::T, y::T) where {T<:Number} = true is requiring that both the x and y parameters be numeric types of some sort.

But then there are other functions that will look like function f(X::AbstractMatrix{T}) where T without any specification of what T is supposed to be. Can someone explain what that is doing and when I would want to use this syntax?

Upvotes: 9

Views: 2329

Answers (3)

user2138149
user2138149

Reputation: 17170

I believe since the other answers were posted, the Julia language syntax changed such that the where T is now optional.

So far, at least, I haven't seen any cases where where T was required beyond just using a template parameter T as part of some other type, for example Vector{T}. In both cases, they just imply T<:Any.

It is required, however, if you want to use the template parameter T from within the body of the function, as other answers have already stated.

Here are some example functions which you can copy+paste into a REPL to experiment with the syntax. I found these useful to play with.

Example 1

function example(x::Vector{T}) where T
    println(typeof(x))
    println(T)
end

If we call this function with an array of a single type, we get Vector{} of that type. (Notice I did not write Vector{T} here, because this suggests T can vary, whereas here we have a specific type Int64.)

julia> example([1])
Vector{Int64}
Int64

If we call this function with an array of multiple types, we appear to get Vector{Any}.

julia> example([1,2,3,"hello"])
Vector{Any}
Any

Why don't we get Vector{Union{Int64, String}}?

function example1a(x::Vector{Union{Int64, String}})
    println(typeof(x))
end
julia> example1a([1, 2, 3])
ERROR: MethodError: no method matching example1a(::Vector{Int64})

That's a bit weird. Let's try something else.

julia> example1a([1, 2, 3, "hello"])
ERROR: MethodError: no method matching example1a(::Vector{Any})

Now it's obvious what's wrong.

  • Apparently Vector{Union{Int64, String}} is not a supertype of Vector{Int64}. I suppose if you think about it from a type hierarchy point of view, where types are nodes in a tree structure, then this probably is sensible.
  • Perhaps less surprisingly Vector{Union{Int64, String}} is not a supertype of Vector{Any}.

Let's see if we can fix it.

julia> example1a(Vector{Union{Int64, String}}([1, 2, 3, "hello"]))
Vector{Union{Int64, String}}

Ok that works. If we force the vector to be created as the type we need, everything is fine.

By the way, if we want to println(T) in the function body, we can do so.

function example1b(x::Vector{Union{Int64, T}}) where T
    println(typeof(x))
    println(T)
end
julia> example1b(Vector{Union{Int64, String}}([1, 2, 3, "hello"]))
Vector{Union{Int64, String}}
String

Here's another surprising result.

julia> example1b(([1, 2, 3, "hello"]))
Vector{Any}
Any

Example 2

This example does not compile, because we attempted to use T from within the body of the function, without where T in the function signiture.

function example2(x::Vector{T})
    println(typeof(x))
    println(T)
end
ERROR: UndefVarError: `T` not defined in `Main`

Conclusion

I suppose what this demonstrates is that using the type system in Julia in a highly specific way isn't likely to be that useful.

For example, if you define a function which takes a parameter of type

::Vector{Union{some, complicated, thing}}

then it can't be called with Vector{Any} which is the default if you create an array of mixed types.

This is likely to not lead to a very elegant codebase if used excessively, because you will be forced to write many identical methods which differ only in the types of their function arguments.

I suppose that is the opposite of generic programming.

Upvotes: -1

Benoit Pasquier
Benoit Pasquier

Reputation: 3015

Oscar's answer is correct, but to answer your second question,

When I would want to use this syntax?

like mbauman suggested in the comments, a typical use for this syntax is when you want to use the type in the body of the function. Maybe the example in the docs helps:

julia> mytypeof(x::T) where {T} = T
mytypeof (generic function with 1 method)

julia> mytypeof(1)
Int64

julia> mytypeof(1.0)
Float64

Upvotes: 4

Oscar Smith
Oscar Smith

Reputation: 6398

where T without anything else is just where {T<:Any} in other words, it's true for all T, but Julia needs you to write something so that `T is defined.

Upvotes: 9

Related Questions