I'm filing this Issue to follow-up on a conversation @JeffBezanson and I had after his JuliaCon2017 talk The State of the Type System (sorry it took me so long!):
We should consider using a separate keyword for the where T
syntax in the new type system for the cases when there are no variable bounds. For a new user (at least for me and @denizyuret), the current syntax is confusing: it doesn't read as proper english. I keep thinking, "where T what?"
function f(x::Array{T, N}) where N where T
end
I can't help but read that and ask, "where T what?" Even worse, since I expect something to come after it, I find myself parsing the next bit as if where N where T
is all one phrase -- as if where T
is gonna be the bounds for where N
.
This syntax only clicked for me once Jeff got to the later slides. I now understand that the word choice for where
comes from the possibility of bounding the type variable:
function f(x::Array{T, N}) where N where T<:Real
end
Now it makes sense, but before that I was very confused. So Jeff and I discussed after his talk about considering using a different reserved word than where
for the case when you're not bounding the type.
I understand there has already been a lot of bike shedding about what keyword to use for this, including discussions of for
, with
, etc. I don't mean to re-open any wounds, but this seems like a tidy change that might be worth making.
To help guide that discussion some, it might be worth considering when we expect users to use where T
without bounds. Since there's the available syntactic sugar f{T,N}(x::Array{T,N})
, it seems to me that the most useful time to use where T
without a variable bound is for disambiguating scoping, such as this slide from Jeff's talk:
julia> (Vector{Vector{T}} where T) <: Vector{Vector{T} where T}
false
In that case, the purpose those where T
s serve is to scope which types can vary inside this type. So maybe something super explicit like withtype T
, fortype T
, withscope T
, forscope T
, scoped T
, or something similar? Or maybe even two words (like we did with mutable struct
), for type T
, with type T
?
Or, more ideas, maybe given T
, under T
, with T
?
Unfortunately I don't have enough maths to be able to say "oh, it's like this one math thing", but maybe there's a good math term out there for this?
Sorry if I'm digging up an old conversation, but at least a couple people I talked to at the conference found the new syntax quite confusing, and I think this could help. Thanks for your time! :)
After playing a bit, I also see that the where T
syntax is useful when you just want to evaluate a generic type as an expression:
julia> Tuple{A,B}
ERROR: UndefVarError: A not defined
julia> Tuple{A,B} where {A,B}
>> Tuple{A,B} where B where A
It feels like what you're saying here is "The type _Tuple_, parameterized on _A_ and _B_, with _A_ and _B_ as type variables".
Not sure how to capture all that in one word.
where T
is just shorthand for where T<:Any
, which reads like prose. It seems unnecessary to me to reserve another word for a case that's already a shorthand.
So I kind of agree with the OP. Probably 90% of the time in the wild, we're going to see naked where
clauses (without the <:
) and most users are not going to understand the etymology that it's short for where T<:Any
. Without that knowledge, it does read quite cryptically.
I guess I don't find it that cryptic. We could definitely try to make it easier to locate in the documentation though.
(It's even stranger when one sees something like f(a::Array{T}) where T = 1
, but in this case not even another word would solve it.)
For one-line function definitions, the preferred form is f(a::Array{T}) where {T} = 1
, which provides better visual separation between the typevar and the function body.
I tend to like how
@exists T ->
function f(x::T, y::T)
...
end
reads, and I think this parses already, so one could make a macro to do this when things would look ugly with where
. It also stacks very well, like in
@exists T ->
@exists (U <: AbstractArray{T}) ->
function f(x::U, y::T)
...
end
and can be used one-line in
@exists T -> f(x::T) = x
Of course this is extensible also to the syntax
@exists T -> Foo{T}
which is quite similar to the standard notation for this in type theory (since ->
traditionally takes the place of .
in Julia).
Finally, if β
parses as a unary macro, it can be used for this too. Then we can have βT -> f(x::T) = x
π.
Also,
function f(x::T, y::T) β T
...
end
julia> (Vector{Vector{T}} β T) <: Vector{Vector{T} β T}
false
I like @NHDaly's "given"?
function f(x::T, y::T) given T
...
end
I dislike β because UnionAll
is the same idea as "there exists"; whereas β would imply the intersection of all.
for example,
abstract type Animal{NumberOfLegs, NumberOfEyes} end
const Biped = Animal{2, NumberOfEyes} where NumberOfEyes
then
x isa Biped
means "there exists NumberOfEyes
such that x is a Animal{2, NumberOfEyes}
", not "for all NumberOfEyes
, x is a Animal{2, NumberOfEyes}
".
On the other hand, β
is perfectly consistent...
β T -> f(x::T, y::T) = x + y
can read as "this method accepts arguments (x, y) if there exists T such that x isa T, y isa T".
For one-line function definitions, the preferred form isΒ
f(a::Array{T}) where {T} = 1
, which provides better visual separation between the typevar and the function body.
FWIW, I still have to re-read that once to parse it correctly. My initial parse is that where {T} = 1
is a single item, and then I have to remind myself that it isn't.
On the other hand, I'm not sure a better "single-word" version of where
would help here. Maybe? But I'm not sure.
Another suggestion: for some T
, or forsome T
forsome
seems promising; it reads pretty well and avoids "stealing" a single word.
FWIW Crystal uses forall
in this case.
Though I firmly maintain that I think we should stick to where
. Changing the spelling for an already concise case is just churn, and adding an alternate spelling for the same thing just makes everything inconsistent. We just need to make sure that it's easy to figure out what where
is and what it means in the documentation, just like any other language concept.
forsome
reads as: for some T this is the case, for others it isn't. forall
seems to imply that it is the case for all T, which it isn't, if there are other more specific methods. I like given
, and given T<:MyType
also reads really nicely. But to my non-native eye, where T
parsed just fine, after I understood what it meant.
template<T, U> f(x::T, y::U) = ...
Just kidding :smile:
Closing this issue. I think everyone is happy with the current state of the world, where where is spelled where. :)
Most helpful comment
Closing this issue. I think everyone is happy with the current state of the world, where where is spelled where. :)