Yes, you should be able to omit names, but you can also distinguish given Ord[Int] and using Context versus given intOrd: Ord[Int] and using ctx: Context.
I think the simple cases look maybe a bit better, but I find that the more complex cases where givens also have using clauses are more difficult to read with the arrows. I think I like the current syntax better.
edit: for example, I find the following harder than what it was before:
given [A] => (inst: => K0.CoproductInstances[Eq, A]) => Eq[A] as eqSum
In fairness the community build examples were translated mostly automatically, without looking at formatting. This example is admittedly more tricky because of the call by name parameter, which is a rare special case in general. If it was not for that, it would look like this:
given [A] => K0.CoproductInstances[Eq, A] => Eq[A] as eqSum
Whereas the current syntax would be this:
given eqSum[A](using K0.CoproductInstances[Eq, A]) as Eq[A]
I find the new syntax better since it concentrates on the essential: I can get an Eq from ProductInstances. By contrast, the name of this given does not matter, and the fact that
the parameters are a using clause is redundant since no other clauses would be allowed.
I agree with the line of reasoning, and find both the newly proposed syntax and the older proposed syntax vastly better than the Scala 2 syntax. So won’t complain much. But I also agree with @Sciss that it is harder to spot the actual ‘final type’ of the given.
Putting the name at the end is inconsistent with everything else.
Scala’s greatest strength is its internal consistency. The most persistent criticisms of Scala are ways in which it is inconsistent or too flexible leading to inconsistency.
I still have no idea why any of this is an improvement over
I find the argument of consistency quite convincing. Even though it might be already late, may I ask, why was the : dropped? I’m asking, because if we kept : for givens, we wouldn’t have to deal with as and how it’s used in pattern matching – very different use case.
It would the look like this, staying close to def and val work and thus staying consistent.
trait Ord[T] {
...
}
given intOrd: Ord[Int] {
...
}
// or
given Ord[Int] {
...
}
given listOrd[T](using Ord[T]): Ord[List[T]] {
...
}
// or
given [T](using ord: Ord[T]): Ord[List[T]] {
...
}
This approach basically only removes the names of the variables and uses given instead of val/def/… and so it should be easy to teach/explain.
My takeaway is that prefix as is not that bad, really. It’s biggest downside is for anonymous conditional instances, where the as connective does not really make sense. But one might get used to that.
So rather than opening up the discussion again, let’s do a poll. If you prefer the existing syntax, please like this post.
There is a semantic ambiguity with =>: In given S => T => U, it is impossible to know whether the given instance type is S => T => U or T => U or U.
The type ?=> does not help: In given S ?=> T ?=> U could mean a given instance of the type S ?=> T ?=> U or T ?=> U or U.
We need to separate the instance type from its requirements explicitly with keywords.
The as approach does not suffer from the problem. However, the anonymous version needs improvement and the using in the condition block is unnecessary.
I like the new syntax. I think all the argumentation for it makes sense and the screenshots very vividly demonstrate the importance of syntax highlighting: the final type of given is easy to spot even if it’s followed by as with a name. And overall, IMO it looks lighter, easier on the eyes.
At this late stage, I don’t have strong feelings about which I personally prefer, though I’ve disliked Scala’s syntax for self-types for as long as it’s existed in its current form, and “[type] as [name]” style might offer a possible improvement here.
Here’s the current syntax:
trait Orange() { orange: Fruit =>
// ...
}
which allows this to be renamed to orange (and hence not be shadowed by other potential thiss in nested classes), and also assigned a type Fruit as a “promise” that Orange must have Fruit, or one of its subtypes, mixed-in before it is instantiated.
I think that could be better written as,
trait Orange() as orange requires Fruit:
// ...
which would avoid the syntax where the body of a template looks like a lambda. The requires keyword would be optional if only a self-name is required, and not a self-type, and it was the syntax used for self-types up to roughly Scala 2.2ish, before the current style was introduced.
I don’t want to complicate this discussion, but I think that even though self-types are not so frequently used any more, they fall into the category of things that are optionally named, and could benefit from having syntax that is more consistent with other optionally-named things in the language.