A most harrowing collection of Julia WATs

Ouch, that one is tough. The kwarg semicolon is a good habit.



In [2]: sum(i for i in range(1,3), start=0)
  File "<ipython-input-2-fe555a1530ed>", line 1
    sum(i for i in range(1,3), start=0)
SyntaxError: Generator expression must be parenthesized

Python also allows omitting the parentheses when the generator comprehension is the sole argument, so I think Python’s parser just recognizes the comma as never part of the comprehension (nested loop is written as for i in A for j in B). We can’t do that for Julia because for i in A for j in B makes a 1D array in a 2-layer loop while for i in A, j in B makes a 2D array (though it only seems to work if there is 1 for, 2+ fors falls back to the 1D array). So if we must allow the comma for the multidimensional comprehension, the remaining way for the parser to catch the typo and throw an error is if j=B is not allowed in comprehensions (or for-loops, to be consistent). EDIT: Actually, that won’t be enough, because in is also a binary operator, so i for i in 1:3, j in 1:4 could also be mixed up with 2 separate arguments (i for i in 1:3), (j in 1:4). Just so much syntax overlap that typos aren’t unambiguous enough to throw an error.

julia> (Tuple{T,T} where T<:String) <: (Tuple{T,T} where T >: String)

julia> (Tuple{String,String}) <: (Tuple{T,T} where T >: String)

Confusing yes, but you have to remember that Union{} is a subtype of every type. It has no instances yes, but the type exists.


Would someone kindly explain why this is?

I think Stefan means that because Union{} is a possible subtype of String and I’ve used the >: superset operator on the right hand side, the middle <: has to return false as it doesn’t always hold theoretically, because

julia> Union{} >: String

Even though in practice there is no possible subtype of String, and it practically holds in all real cases.

I don’t understand why this is useful… but I guess some type theoretic reason the compiler needs the Union{} bottom.

1 Like

I still don’t get it, since Union{} is not a valid tuple field type:

ERROR: Tuple field type cannot be Union{}

so it should be ignored for the purposes of the expression above.

I think this is a bug and you should open an issue. (But let’s wait for others, I am not an expert on darker corners of the type system).

1 Like

EDIT: this is now fixed (in a PR, soon to be merged, I guess). See Intransitivities in subtyping

It’s AFAIK not possible, even in theory, to implement the subtyping (<:) so that it always returns true when a human would expect that.

The discussion in this issue is relevant: suboptimal subtyping for `UnionAll` and `Union` · Issue #52818 · JuliaLang/julia · GitHub

Quoting vtjnash, emphasis mine:

The type system is generally permitted to return false whenever it wants to, provided only that it preserves transitivity.

You might also be interested in the Julia subtyping paper:

Julia Subtyping: A Rational Reconstruction . Proceedings of the ACM on Programming Languages, 2018, 27, ⟨10.1145/3276483⟩. ⟨hal-01882137


Unprovable judgments. Julia’s subtype algorithm, and in turn our formalization, cannot prove all judgments expected to hold.

This is actually a recent bug, introduced in v1.10, see "Tuple field type cannot be Union{}" error in 1.10rc · Issue #52385 · JuliaLang/julia · GitHub


A relevant PR, to expand the <: doc string:

This thread/post would be a great source of cases for a “clippy” style linter. Rusts linting is top class and we should definitely learn from it, to help avoid errors that people might stumble upon, but that can’t be fixed by changing syntax/parsing.

1 Like

In principle I agree with you, but TBH I’m not sure if VS Code ever pointed out an actual issue to me (except syntax errors), while it sadly points out many false positives. IMO the linting for Julia needs to drastically cut down on the false positives if it’s to be useful, instead of just ignored. Although, I think I may actually have already disabled some of the linting options in the extension settings, so maybe my experience is outdated.

1 Like

10 posts were split to a new topic: Intransitivities in subtyping

I completely agree, the current VSCode linter needs work. I’m just a user, I’ve never worked on linting, and I appreciate there must be huge number of challenges, especially in a much more dynamic/flexible language like Julia.

However, I think linting is a very useful tool and its a shame that we don’t seem to have a good linting ecosystem.


Given the refusal to construct Tuple{Union{},Union{}} I would agree with that. Otherwise I don’t see how that subtype check is false.


Here’s a confusing one, from the Julia subtyping paper:

julia> (Vector{T} where {S,T}){Int}  # WAT?
Vector{Int64} (alias for Array{Int64, 1})

Naively one would expect for the type application to apply Int to the unused type parameter. However the unused type parameter seems to have already been deleted/rewritten out by that point.

I think unused type parameters are, thankfully, already caught by the static analysis in VS Code.

The unused S is just discarded at runtime:

julia> Vector{T} where {S,T}
Vector (alias for Array{T, 1} where T)

Also occurs in where statements with non-UnionAll types:

julia> Integer where {A,B,C,D}
julia> struct A{T,S} end
julia> bar(::Union{A{T,2}, A{1,T}}) where T = T
julia> bar(A{1,2}())

I think I would prefer if that was an ambiguity error (or at least a warning when running under special command-line flags or debug builds), such as e.g.

julia> baz(::A{T,2}) where T = T
baz (generic function with 1 method)

julia> baz(::A{1,T}) where T = T
baz (generic function with 2 methods)

julia> baz(A{1,2}())
ERROR: MethodError: baz(::A{1, 2}) is ambiguous.

Ambiguous pattern matching of existential type parameters is imo just as bad as ambiguous method dispatch.

1 Like

This thread has been going on for 2 years and has become an unorganized grab-bag of unrelated discussions. As such, it’s a prime case for Time limits for unfocused discourse threads?.


I very much disagree. Threads like this and What is in your startup.jl? are best when they’re continuously added to. I pretty regularly come back to both of these just to see what people have added. It’s fun – maybe not to everyone, but to some people. And it’s only wasting the time of the people who choose to come here and have their time wasted. It might not be “productive”, but that doesn’t mean there isn’t any value in it.

Do these sometimes spark full-fledged discussions that are unrelated to the main topic? Yes. Should those discussions be migrated to their own topic? Yeah, probably. But that’s just natural conversation. If we want this to work more like StackOverflow, we should be there instead.