TransWikia.com

Difference between `where` keyword inside or outside the curly braces

Stack Overflow Asked by miha priimek on November 4, 2021

What is the difference between

function foo(a::Adjoint{Float64, Matrix{T}} where T)
    return 0
end

and

function foo(a::Adjoint{Float64, Matrix{T} where T})
    return 1
end

(Note the location of the curly braces.)

julia> methods(foo)
# 2 methods for generic function "foo":
[1] foo(a::Adjoint{Float64,Array{T,2}} where T) in Main at REPL[247]:2
[2] foo(a::Adjoint{Float64,Array{T,2} where T}) in Main at REPL[248]:2

It seems in both cases the function would accept an adjoint of Matrix of type T? I can’t figure out what the difference between the two functions is.

2 Answers

Here is an example that illustrates the difference, for the simpler case of Vector{Vector{T} where T} vs Vector{Vector{T}} where T.

First make some type aliases:

julia> const InsideWhere = Vector{Vector{T} where T}
Array{Array{T,1} where T,1}

julia> const OutsideWhere = Vector{Vector{T}} where T
Array{Array{T,1},1} where T

The default array constructor promotes elements to a common type if possible:

julia> x = [[1, 2], [1.2, 3.4]]
2-element Array{Array{Float64,1},1}:
 [1.0, 2.0]
 [1.2, 3.4]

julia> x isa InsideWhere
false

julia> x isa OutsideWhere
true

But by using a typed array literal we can avoid the automatic promotion:

julia> y = ( Vector{T} where T )[[1, 2], [1.2, 3.4]]
2-element Array{Array{T,1} where T,1}:
 [1, 2]
 [1.2, 3.4]

julia> y isa InsideWhere
true

julia> y isa OutsideWhere
false

Answered by Cameron Bieganek on November 4, 2021

The first is a unionall type, the second is a concrete type:

julia> isconcretetype(Adjoint{Float64, Matrix{T} where T})
true

julia> isconcretetype(Adjoint{Float64, Matrix{T}} where T)
false

julia> (Adjoint{Float64, Matrix{T}} where T) isa Core.UnionAll
true

The first one is the infinite set of adjoints wrapping a Matrix of some type. In other words, we could imagine representing the infinite set in pseudocode with something like:

Set([Adjoint{Float64, T} for T in all_possible_types])

Whereas the second is an adjoint wrapping a Matrix of some type, or in other words:

Adjoint{Float64, Matrix_of_any_type}.

You almost always want the former, not the latter. There is little reason to construct an adjoint which can contain any Matrix, usually you just want an adjoint with one type.

The difference is clearer with Vector:

  • Vector{Vector{T}} where T is a unionall representing all vector of same-type vectors.
  • Vector{Vector{T} where T} is a vector containing the specific element type Vector{T} where T.

Answered by Jakob Nissen on November 4, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP