If you prefer the syntax with =>
and postfix as
instead, please add a like to this post instead.
My takeaway from that screenshot is that colon as line continuation character can sometimes be in competition with colon as type introducer
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 this
s 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.
The more I look at it, the more I think the former syntax is better. It is visually more distinct what part of the definition is what - name, parameters (by using using
), return type - compared to arrows to arrows to arrows. I also expect all these arrows won’t be appreciated by the large part of the Scala community that are not active on forums like these. More symbolic operators make it look more arcane and I expect also harder to explain.
Since we also don’t have much time to get to use this syntax and tweak it further based on experience I am not in favor of introducing this change.
In my very humble opinion the new proposed syntax does a better job at setting givens apart as a distinct feature. The current syntax looks a bit like a word salad that wants to be different from a regular def
but doesn’t quite manage it yet. You have given
, then something method-like with a redundant using
that seems to be inherited from implicit def
when it was still possible to have both implicit and explicit parameters. Then instead of being consistent and going with the method-like thing and writing the type ascription with a :
there’s the out-of place as
—which apparently only exists because :
conflicts with the optional “optional braces” thing.
I’m being a bit hyperbolic for the sake of the argument here.
I understand the reasoning and I agree that it’s good to have given
s be distinct from normal functions/methods. I just think that all these arrows will make it hard for new developers / scala 2 developers to see what’s exactly going on.
given [T] => Ord[T] => Ord[List[T]] as listOrd = ...
Intuitively I think this looks more like a curried function from some generic T
to a Ord[T]
to a List[Ord[T]]
instead of: I have some generic T
, I expect an Ord
for that T
, and I will give you a Ord[List[T]]
.
I wouldn’t mind toying with some more ideas, but there is very little time for feedback/experimenting with these ideas. And I think what we have now is good enough (even if there are always things to improve).
Well you don’t have a T
. The arrow-based syntax says precisely what it should: give me a T
, give me an Ord[T]
, and I’ll give you an Ord[List[T]]
. This can be understood following the usual Curry-Howard isomorphism: the given
declaration is a constructive proof that if you have some T
and some Ord[T]
, then there exists a corresponding Ord[List[T]]
value. This is in line with how implicits have been used for automatically synthesizing type-level proofs. Fittingly, the fat arrow looks like mathematical implication.
What if the as
keyword is replaced by something else, without changing the syntax structure?
given foo is Foo[A]
given foo(using Context) is Foo = ???
case foo is Foo() =>
// or
given foo naming Foo[A]
given foo(using Context) naming Foo = ???
case foo naming Foo() =>
// or
given foo for Foo[A]
given foo(using Context) for Foo = ???
case foo for Foo() =>
So my poll was almost a draw. 12 votes for existing syntax vs 14 for new. Which tells me that neither syntax is fully convincing.
One interesting question is whether the given syntax should be consistent with the rest or intentionally different. My tendency would be to say it should be consistent as long as it does not give up any benefits.
With that in mind, I have explored a different way of doing things that is even more consistent than the current syntax. In particular, it drops as
in favor of :
.
I won’t copy the very long PR message here but comments are welcome on this thread.
I really like this. Best proposal I’ve seen so far, IMO. Much more consistent.
Yes!!! Although I like to lurk and skim, I often don’t devote much mental power. For I think the last 2 years or so, I’ve been looking at the various given
syntax proposals but in my low-mental-power mode I found nearly all of it unclear and confusing. “I’ll pay attention when it’s finalised” I’d think. I just now read the new proposal and immediately found it very clear and very comprehensible, even in my 6am brain-dead state. I find it’s so much more consistent with all the other types of definitions that I’m used to in Scala, I love that term: Type
is back in use here, and I love that the cost of changing definitions from implicit or not, is nice and small just like it is an Scala 2. So an emphatic from me.
I love it! This is the best scheme I’ve seen so far. It should be easy to migrate to by Scala 2 developers and to teach to newcomers to Scala at large. Thank you Martin
Thank you Martin for reconsidering, this new syntax looks very good to me. I find it very readable, and also I didn’t like the double meaning of colon as line separator for indentation.
Even with this proposal, colon is still used to mark indentation when defining a class/trait/object with the indentation syntax.
Yay for consistency