Switch order of type and term in `as` definitions?

If you prefer the syntax with => and postfix as instead, please add a like to this post instead.

20 Likes

My takeaway from that screenshot is that colon as line continuation character can sometimes be in competition with colon as type introducer

10 Likes

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.

5 Likes

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.

3 Likes

3 posts were split to a new topic: Scala 3 significant indentation

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.

2 Likes

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.

4 Likes

I understand the reasoning and I agree that it’s good to have givens 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).

4 Likes

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.

1 Like

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.

21 Likes

I really like this. Best proposal I’ve seen so far, IMO. Much more consistent.

8 Likes

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 :+1: from me.

16 Likes

100% agree with @japgolly. All in favour of this latest given syntax.

5 Likes

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 :clap:

7 Likes

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. :sunny:

3 Likes

Even with this proposal, colon is still used to mark indentation when defining a class/trait/object with the indentation syntax.

1 Like

What about marking these indentation separations using with as well? @odersky

4 Likes

Yay for consistency :slight_smile:

1 Like