Highly subjective but to me, mySpecialName as SomeType looks semantically backwards and somewhat like a type cast[1]. The other way around, given SomeType as mySpecialName, has some history in at least the form of SQL-aliases. It also flows more naturally from the fact that the required bit — the type — comes first, followed by the bit that can be left out.
Seconded. Infix as is used for casts in several language, including TypeScript and Kotlin, so it’s a pretty mainstream usage.
Having the name on the right also looks better for patterns. The reason given for not having it on the right for pattern was “consistency with the given syntax”… So I’d argue to change both.
There is currently a strange inconsistency with given and as, which I encountered several times.
We can write the following:
given[A] as Foo[A]
But then if we try to remove the type parameter, it doesn’t work:
given as Foo[Int] // error: end of statement expected but identifier found
Instead, we have to write:
given Foo[Int]
But that syntax doesn’t work with a parameter:
given[A] Foo[A] // error: `as` expected
That seems very weird to me. Wouldn’t it be better to do it the following way?
// Without parameters:
given Foo[Int]
// With parameters:
given[A] Foo[A]
// Without parameters, named:
given Foo[Int] as foo
// With parameters, named:
given[A] Foo[A] as foo
It seems more elegant and consistent, and does not violate the definition before use principle.
let RegisterControl(control:Control) =
match control with
| :? Button as button -> button.Text <- "Registered."
| :? CheckBox as checkbox -> checkbox.Text <- "Registered."
| _ -> ()
It would be a bit crazy to do this at this very last minute. We are 3 weeks away from RC-1. Even so, I believe your arguments have merit. I am not worried about changing as order in pattern matching, since that’s relatively minor. But changing the syntax of givens again is a big ask!
Just for the sake of the argument, if we do it, then I don’t think we should write
given[A] Foo[A]
The fact that [A] is attached to the given keyword is a glaring lexical irregularity. The alternative
given [A] Foo[A]
isn’t any better. Why is Foo between two [A]'s? Why is there a space on the left but not on the right? So, I don’t think this will fly. If we do it we’d have to make it clearer what is a formal parameter and what is an actual argument. The as keyword served to distinguish the two. We did explore => once before, and I still think that’s the best alternative. I.e. it would be
The arrow looks better than the status quo to me. Less potential for confusion.
given [A] => Foo[A]
given [A] => Foo[A] as FooA
given [A] => Ordering[A] => Ordering[List[A]] as ListOrd
It looks exactly like a polymorphic function type though, but I think that’s conceptually adequate. Maybe in the future Scala can support first-class-polymorphic implicits, so that the following two lines would effectively become equivalent:
I think something like that could work, but unfortunately we want the lambda syntax to allow omitting the type, so that type inference can, well, infer it. Potentially we could allow that with syntax like (using _ as ctx) => ..., but that would be yet another use of underscore in Scala.
Overall there are 207 changed files. To get an impression about the impact of the changes, go to https://github.com/lampepfl/dotty/pull/10489/files and look at changes in .md files and in the community build.
Looking at the changeset of the PR, I have the impression that the new syntax is more regular and also reads better. What do others think?
is it really better?
is it important enough to switch at this late stage? If we do it, we will certainly need a try-out period of 4-8 weeks where people can give feedback.
One thing we have to be clear about is that we will not introduce one syntax now and change it in the next release. Whatever we decide now we have to stick with.
The current PR changes given and pattern syntax. It leaves using clauses alone. They are still written (using Context) or (using ctx: Context), not (using Context as ctx). I was considering changing them as well. The upside would be to have a universal rule that optional identifiers of some specified type are always bound with as. But there are downsides as well:
Less concise. This matters because typical programs will have only a few givens but many using clauses.
Less regular when seen in conjunction with normal parameters. Most methods will have both normal parameters and using clauses. It looks weird to have different syntactic conventions for them.
Changing the order for given doesn’t make sense IMO. Before we had Scala style (name followed by type), now we have Java style (type followed by name)
given intOrd as Ord[Int]
object intOrd extends Ord[Int]
val intOrd: Ord[Int]
vs
given Ord[Int] as intOrd
public foo (Ord[Int] intOrd) {}
public Ord[Int] intOrd;
I think the problem is that semantically when you say “given foo as bar”, that’s really different from “case bar as foo”. Perhaps the whole @ -> as replacement is not a good idea.
IMHO both pattern binding and givens look better in this new syntax.
- case c as ClashNoSig(y) => c.copy(y + c._1)
+ case ClashNoSig(y) as c => c.copy(y + c._1)
- given listOrd[T](using Ordering[T]) as Ordering[List[T]]
+ given [T] => Ordering[T] => Ordering[List[T]] as listOrd
The important parts are placed in first position.
By the way, I noticed that given => Int seems to be a new syntax introduced by the PR, specifically for “by-name” givens, as seen in tests/run/given-mutable.scala :
given => Int =
var x = 0
x += 1
x
@main def Test =
assert(summon[Int] == 1)
assert(summon[Int] == 2)
It does seems useful to distinguish by-name and by-value givens. For the same reason we distinguish val x = ... from def x = ....
As the person who originally suggested swapping the order, I must say that @Sciss makes a good point that having the order different between basically equivalent constructs in the same Scala language is very inelegant.
I think my opinion has changed since my original suggestion, and currently I think that <name> as <type> makes the language look more regular than the reverse
Java does not have anything like pattern binding or type-based implicit resolution. In both, the name coming after as is completely optional and is not the important part. This is to contrast to things like val bindings, where the type is the optional part and the name is not. So the different orders in these different constructs seems to make sense to me.
I think many people actually suggested it at the rime :^)
Honestly, I am against any last minute changes based on the criterion of looks better. This is super subjective and I can guarantee that we can find situations that both options look better in.
We are also already working on scalafmt and any new change is a bit of a wasted effort for us. However, if you decide to indeed change the syntax let us know exactly what is changing, because I am finding it difficult to keep up.