Since we are close to feature freeze, I'd like to discuss the last remaining point that remains open for me. Should we use := instead of = for assignments? I left it lying for a long time since I hesitated to wake up a sleeping dog. Almost everybody else uses = for assignment. So the path of least resistance is to continue using = in Scala. However, now that I am actually very happy about the syntax we have achieved, using = for assignments sticks out like a sore thumb. So before accepting the status quo, I wanted to at least make the argument why one would want to switch.
Consider:
def incr = x = x + 1
This is terribly wrong on several levels. It's hard to parse and uses equals for two fundamentally different things. By itself, x = x + 1 makes no sense either, and requires embarrassing explanations for everyone new to programming.
Other languages don't have the problem to the same degree, since they don't use = for definitions. For instance, in Java or Python, you could read = as _always_ meaning assignment. The incr example looks like this in these languages:
int incr() {
x = x + 1;
}
def incr():
x = x + 1
But in Scala, we do use = for definitions, which makes the abuse of the operator in assignments so much worse.
:= is a natural alternative. It's used in every serious treatment of imperative programming logics and is also used in quite a few languages, including Algol, Pascal, OCaml, F#, Go. Using := instead of = makes imperative operations visually more distinct from functional ones, which is a good thing.
:= can be treated by the same rules as operator assignments. That is, in a := b, if the left hand side has an applicable method named := the assignment is rewritten to a.:=(b). I believe we can roll the existing syntactic expansion of = to update calls into the same logic, which would simplify the desugaring rules.
Migration: Scala 3.0 should allow = and :=. = would be deprecated at some later version. This will probably take some time.
Could you clarify if array(i) = x would stay the same, or be written array(i) := x?
I believe it should be written array(i) := x.
This change does not play well with the desugaring of op= operators:
i += 1
now desugars into
i := i + 1
while
xs +:= 5
desugars into
xs := xs.+:(5)
Poking at this, it sounds like you're suggesting := for assignment, but leaving = for definition. This raises several questions.
Most obviously, what does a var declaration look like? Are we saying:
var x := 1
or
var x = 1
Either way, it winds up feeling rather asymmetrical against:
val y = 2
x := 3
And @sjrd beat me to talking about +=.
While I'm not dead-set against this one, I think I'm against trying to rush it into 3.0 this late in the process. This would have to happen slowly, over several releases, anyway, so I don't think it qualifies for the "rewrite all the textbooks" argument. I'd recommend putting this one off and giving it sufficient time for thought and experiment...
I believe a var declaration should also use :=.
var x := 1
One can argue either way, but ultimately, that's the safe choice. The fact that it's different from val x = 2 is actually a good thing, since val and var are too easily confused today.
Operators: I don't see the problem. x op= y desugars to x := x op y, unless op is : in which case it stays as it is.
What would you suggest to user-land code that made use of :=, like sbt? Outside of being able to backtick-escape :=.
Yes, there's a ton of DSLs that heavily use :=, besides sbt I can think on top of my head of chisel (https://www.chisel-lang.org/), scalatags (http://www.lihaoyi.com/scalatags/) and scalajs-react (https://japgolly.github.io/scalajs-react/#examples/ajax-1).
@dwijnand That is addressed by
:=can be treated by the same rules as operator assignments. That is, in a := b, if the left hand side has an applicable method named := the assignment is rewritten to a.:=(b).
Hum but then how do you actually assign something to a var whose type has a := method?
Another thing: this will not work well with setters defined as
def x: Int = ...
def x_=(v: Int): Unit = ...
How do you explain that
foo.x := 5
desugars into
foo.x_=(5)
and not
foo.x_:=(5)
Yes, setters are an issue. For compatibility we probably have to leave them as x_=, even though it's not ideal.
Hum but then how do you actually assign something to a var whose type has a := method?
You mean a := method that is applicable to the right hand side, i.e. to the type of the variable itself? That looks like a pretty extreme and nonsensical case. If you do that then maybe you should not be able to assign to such a var.
If you do that then maybe you should not be able to assign to such a var.
Or maybe you're forced to backtick-escape:
var foo = SettingKey[String]("foo", "")
foo := SettingKey[String]("foo", "Use foo to foo")
foo `:=` "bob"
Or maybe you're forced to backtick-escape:
Yes, maybe we can add a special rule that backtick-escape always means straight assignment. But I'd like to see some actual use cases first.
I don't think any special rule is necessary - wrapping the LHS in parenthesis (i.e. (x) := 5) suffices to disambiguate assignment vs calling a method.
What about extension methods (currently via implicit classes) that use :=?
I don't think any special rule is necessary - wrapping the LHS in parenthesis (i.e. (x) := 5) suffices to disambiguate assignment vs calling a method.
No, since the priority goes the other way: if the variable's type has an applicable method := the method call takes precedence.
What about extension methods (currently via implicit classes) that use :=?
It's _exactly_ the same rules as for assignment operators. So, extension methods are supported.
FWIW, I develop a DSL that uses :=. I created a macro to that rejects DSL terms from being assigned to var, so the user cannot write var foo = DSLTerm(). Thus the logic for this proposal is sound, IMO, but I'm worried maybe not all DSLs follow this concept.
Using := instead of = makes imperative operations visually more distinct from functional ones, which is a good thing.
Just want to add that Scala already achieves this: initialization/definition and mutation can be distinguished in syntax easily. Given any code snippet, it's easy to find all mutations _on the first sight_. This is not true in Java, where an immutable final field can be initialized via assignment.
For language ergonomics, when there is no syntactic ambiguity and semantic ambiguity for the compiler and for programmers, reuse of the same symbol may be beneficial. For example, in natural languages:
In each sentence, the meaning of is is different.
I don't mind this change either way, as long as things like Scalatags or Scala-Js-React do not break, but this line sticks out:
That is, in
a := b, if the left hand side has an applicable method named:=the assignment is rewritten toa.:=(b).
Does that mean a var thing: {def := } could never be mutated? That doesn't seem like a particularly good outcome. In particular, the way update is de-sugared isn't comparable at all because it is ambiguous: foo(x) = y cannot be anything else other than an .update call.
Maybe var thing: {def := } is an uncommon scenario to be in, but we shouldn't make uncommon scenarios impossible
Can we not change things unless there is a really, really strrong reason?
def incr = x = x + 1
This is terribly wrong on several levels. It's hard to parse and uses
equals for two fundamentally different things. By itself, x = x + 1 makes
no sense either, and requires embarrassing explanations for everyone new to
programming.
I don't buy this argument.
(Technically, IIUC := is more appropriate for a definition than for
reassignment, but of course it would be even worse to change definition
syntax.)
On Thu, Nov 21, 2019 at 10:28 PM Li Haoyi notifications@github.com wrote:
I don't mind this change either way, as long as things like Scalatags or
Scala-Js-React do not break, but this line sticks out:That is, in a := b, if the left hand side has an applicable method named
:= the assignment is rewritten to a.:=(b).Does that mean a var thing: {def := } could never be mutated? That
doesn't seem like a particularly good outcome. In particular, the way
update is de-sugared isn't comparable at all because it is ambiguous: foo(x)
= y cannot be anything else other than an .update call.Maybe var thing: {def := } is an uncommon scenario to be in, but we
shouldn't make uncommon scenarios impossible—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/lampepfl/dotty/issues/7598?email_source=notifications&email_token=AAAYAUBJDC5J3GNJB7O5PFTQU5GWJA5CNFSM4JQA26SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEE4NHYQ#issuecomment-557372386,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAAYAUEMFC3PYOOIXJUDYFTQU5GWJANCNFSM4JQA26SA
.
Does that mean a var thing: {def := } could never be mutated?
We had a discussion about this above, but for clarity let me add the full types:
class Ref[X] { def := (x: X): Unit = ??? }
var x, y: Ref[Int] = new Ref[Int]
x := 1 // calls the method
y := x // is a primitive assignment, since the method is not applicable
We would make assignments impossible if we defined Ref like this:
class Ref[X] { def := (x: Any): Unit = ??? }
_Maybe_ we need an escape hatch for these situations such as putting := in backticks. But I assume it's such a weird use case that we do not need to worry about it.
I like this proposal. In Haskell, the use of = is confusing in places (data Foo = Bar | Baz). It'd be nice to see Scala clean up on this ambiguity.
One small thought: Scala excels at DSLs, and var is just one type of mutable reference: AtomicReference is another (with get / set / update capabilities), and ZIO's Ref provides yet another.
It'd be nice if var x became, conceptually at least, if not in implementation (for performance reasons), something like val x = new Var[Int], such that val is the only conceptual primitive and all var assignment and mutation are just syntax for method calls on objects.
This would open the playing field to alternate versions of var such as one built on atomic reference that provides visibility and synchronization guarantees:
val ref = new AtomicRef[Int](0)
ref := 1 // ref.:=(1)
ref := 2 // ref.:=(2)
ref += 4 // ref.update(_ + 4)
In my opinion the cost of introducing this change (code, corner cases, making people like it and use it) is bigger than the gains from this change.
It'd be nice if
var xbecame, conceptually at least, if not in implementation (for performance reasons), something likeval x = new Var[Int], such thatvalis the only conceptual primitive and allvarassignment and mutation are just syntax for method calls on objects.
This was once implemented in the Virtualized Scala fork.
Will the return type of new assignment operator still be Unit or will it return the type and value of value assigned allowing for while (c := inputStream.read(buf) != -1) { syntax? This change could mirror Python's PEP 572 - assignment expression and bring similar functionality into Scala.
@lbialy I think this pattern is an ugly hack for imperative languages to make up for the fact that they don't have an expression-based syntax. In Scala, you'd just write:
while ({ c := inputStream.read(buf); c != -1 }) { ... }
And with the new control syntax it would become the more elegant:
while { c := inputStream.read(buf); c != -1 } do { ... }
Another option is to use <-. In R you can assign variables like this:
var1 <- 11
or
22 -> var2
Maybe we need an escape hatch for these situations such as putting := in backticks. But I assume it's such a weird use case that we do not need to worry about it.
I think we need an escape hatch. "oh you cannot ever assign vars of these types, due to syntactic reasons" seems a pretty good reason to disqualify a syntax entirely. Having things like a *= b multiple-and-assign syntax not work in some cases is fine, because people can fall back to the full a = a * b. When a = b doesn't work, what do they fall back to?
We shouldn't be willing to take shortcuts for common cases at the expense of making uncommon cases literally impossible.
To be quiet honest I would really love such a change.
It would additionally discourage mutability and make code easier to read and reason about.
Assignment is an expression in many other languages (for instance in java) and it's a statement returning Unit in Scala. I believe the reason for this was UAP and setters. I am aware of block syntax used with while loop in Scala 2 (it's also source of numerous // format: off in my code). I don't really get the part about how the fact that Scala has a statement where other languages actually have an expression is making that syntax a hack in imperative languages, could you elaborate @LPTK ? My point was only about that if it's possible to make syntax and behaviour more similar to other popular languages it would help with the learning curve for newcomers via the least surprise principle.
@lbialy I think it's a hack because it seems to have been designed specifically to allow for this kind of hard-to-parse while patterns. The designers saw their syntax was often too limited, but instead of generalizing it in a natural way (making it expression-based), they went with this and other things like the ternary ? : operator.
Mutating a variable is not innocuous or harmless; I think it's best not allowed as a subexpression in the middle of a bigger expression, but rather stated clearly in a separate statement.
For me { c :=inputStream.read(buf); c != -1 } looks like ugly hack for Scala's treating as a statement something that every other language treats as an expression. The fact that assignment returns Unit always felt counter-intuitive to me and always will.
Why is there suddenly this need to change everything about the Scala syntax?
I am not against significant indentation, assignation with :=, etc.
But Scala has been using the same syntax for over 10 years.
Compared to given/delegate and those other tweaks to the language I don't see what all those syntax changes will bring as direct benefit except confusion.
Am I in the minority?
As it is stated in the question:
Should we use := instead of = for assignments?
Would this valid syntax for this change:
var x: String := 1
val y: String := x +1
It'd be nice if var x became, conceptually at least, if not in implementation (for performance reasons), something like val x = new Var[Int], such that val is the only conceptual primitive and all var assignment and mutation are just syntax for method calls on objects.
The problem is what to do with dereferencing.
var x := 1
val y = x
x := 2
y // yields 1
whereas
val x = new Var(1)
val y = x
x := 2
y // yields Var(2)
Why is there suddenly this need to change everything about the Scala syntax?
I am not against significant indentation, assignation with:=, etc.
But Scala has been using the same syntax for over 10 years.Compared to given/delegate and those other tweaks to the language I don't see what all those syntax changes will bring as direct benefit except confusion.
Am I in the minority?
Agree. I don't see any benefits it brings except making newcomers more confused.
Why is there suddenly this need to change everything about the Scala syntax?
Good question. When Scala started, it specifically did not want to invent syntax. The default was to do it like Java, except if there was a really good reason to change. That reason existed for using x: T instead of T x and for making semicolons optional (btw both changes were also hotly disputed at the time). Now, we have evolved quite a bit. Scala is no longer primarily an extension of Java, it has pioneered its own usage patterns. Scala 3 deviates more from Java. Having used Scala 3 in anger over the last 2-3 months I can state that it's absolutely, amazingly, fantastic! The new syntax gave me the biggest productivity boost I had for I don't know how long. I would not have expected that at all, but that's what it is. It helps my programming more than all semantic changes taken together. And I was the one who created the old syntax and used it exclusively for 15 years, so that should tell you something. When teaching the language to (currently 250) students, I have the same positive sentiment, it feels like the language constructs hang together a lot better than before, which makes it also easier to teach to newcomers.
Now I can see the counter-argument that I am arguing from my own experience and other people's experience and tastes are different. I accept that argument, but only if others actually gain a comparable degree of experience with the new syntax. Give it a couple of weeks or a couple of months where you use the new language intensively. If this has not increased your productivity, let's discuss. First impressions and reactions don't count, since that could very well be Stockholm syndrome.
That's the general reason for the changes. Now, for assignments, I am not 100% sure, as I wrote at the beginning of this issue. If we had kept largely the old syntax, I would not have proposed it, first since it would deviate too much from the Java theme, and second, since the old syntax was a compromise anyway. But the new syntax feels really natural and pleasing, so the problems with using = for assignment stand out more.
I'm not nearly smart enough to even comment on this thread but I'm just glad Mr odersky puts so much thought in the minutest of things.
I accept that argument, but only if others actually gain a comparable degree of experience with the new syntax. Give it a couple of weeks or a couple of months where you use the new language intensively. If this has not increased your productivity, let's discuss. First impressions and reactions don't count, since that could very well be Stockholm syndrome.
I don't think this is a good way to gatekeep discussion. Most of us do not have the ability to use an experimental fork of a niche language full time, nevermind for a couple of months! After all, we already have full time jobs, and million-line Scala codebases that we're not going to port to dotty in the next week.
This requirement basically limits the discussion to those who work full time at EPFL on Dotty, locks everyone else out, and dismisses years of industry experience as "stockholme syndrome". Not exactly an inclusive, collaborative, or diverse basis to begin a discussion; likely you'll find the only input that passes that bar is your own.
Why is there suddenly this need to change everything about the Scala syntax?
Good question. When Scala started, it specifically did not want to invent syntax. The default was to do it like Java, except if there was a really good reason to change. That reason existed for using
x: Tinstead ofT xand for making semicolons optional (btw both changes were also hotly disputed at the time). Now, we have evolved quite a bit. Scala is no longer primarily an extension of Java, it has pioneered its own usage patterns. Scala 3 deviates more from Java. Having used Scala 3 in anger over the last 2-3 months I can state that it's absolutely, amazingly, fantastic! The new syntax gave me the biggest productivity boost I had for I don't know how long. I would not have expected that at all, but that's what it is. It helps my programming more than all semantic changes taken together. And I was the one who created the old syntax and used it exclusively for 15 years, so that should tell you something. When teaching the language to (currently 250) students, I have the same positive sentiment, it feels like the language constructs hang together a lot better than before, which makes it also easier to teach to newcomers.Now I can see the counter-argument that I am arguing from my own experience and other people's experience and tastes are different. I accept that argument, but only if others actually gain a comparable degree of experience with the new syntax. Give it a couple of weeks or a couple of months where you use the new language intensively. If this has not increased your productivity, let's discuss. First impressions and reactions don't count, since that could very well be Stockholm syndrome.
That's the general reason for the changes. Now, for assignments, I am not 100% sure, as I wrote at the beginning of this issue. If we had kept largely the old syntax, I would not have proposed it, first since it would deviate too much from the Java theme, and second, since the old syntax was a compromise anyway. But the new syntax feels really natural and pleasing, so the problems with using
=for assignment stand out more.
It seems like all the arguments like sentiments and tastes are subjective, but, can someone tell me, why would companies invest money and time, just to cater to someone's taste with pure syntax change? Time and money is not subjective to them at all.
@texasbruce Increased productivity = Time and Money. There would of course be automatic rewrites to keep the cost of change low.
@texasbruce Increased productivity = Time and Money. There would of course be automatic rewrites to keep the cost of change low.
How exactly does replacing = with := increase productivity other than personal feelings?
Even if it provides increased productivity, the arguments for the companies are not so simple. Does the amount of productivity increase justify the amount of time and money spent? Does the complexity created by syntax changes affect the hiring of new assets and future maintenance of existing codebase?
It's not simple equation like that, and Scala, as much as we love it, is not a personal project.
I'm sorry, Martin, but I think you're making a serious mistake here in not seeing this from the viewpoint of the companies (like the one I work in) that have dozens-to-hundreds of Scala engineers who need retraining. Every little bit of change here adds up in a serious and dangerous way, and the syntactic shifts make it all vastly scarier to those communities. Automatic rewriting of the code doesn't help -- it's peoples' brains and habits that are the issue here, and the price of these changes can easily run to hundreds of thousands of dollars in the short-to-medium term. Folks aren't going to casually take your word for it that we're going to recoup that in increased productivity eventually.
When the Scala 3 project started out, the message, loud and clear, was "We aren't going to split the community". I think that, by suggesting so much change at once, you are severely increasing the odds of doing exactly that. Plain and simply, you have scared the heck out of many of us who are Scala evangelists at our companies. I don't disagree with most of the individual changes (although I'm still deeply skeptical about the braceless style), and might well agree with most of them if they were spread out over time. But IMO it's terribly important not to slam it all in at once, or we're likely to lose large numbers of people -- and worse, large numbers of companies who decide that this is too much headache.
We need to have more patience, to recognize that the language is going to keep evolving, and focus on creating a solid foundation in 3.0 rather than a huge big-bang change...
Other people have made the technical arguments, but from an end user perspective I don't see this ending well.
Regardless of how good the individual cases look or how easy it is to rewrite, this change would involve a massive mental shift for the vast majority of programmers who have already internalized the way it works now.
Also regardless of what logics and languages already do this (and Algol/Pascal/OCaml/F#/Go are not languages most Java programmers are familiar with), there will be an inevitable massive backlash against this -- Reddit and Hacker News would justifiably have a field day, because this is the exact kind of tweaking that makes developers lives hard for very little benefit other than "it looks nicer to the language designers this way."
I might be wrong, but the example shown looks like a corner case.
Either way, it would be Scala code which goes against the guidelines since in order to compile x should be a global var
So the question is: why making a change to accommodate for a code style that most Scala engineers would never write?
Assignments are the building blocks of any program while the situation used as an example probably does not represent a common scenario.
What am I missing?
@texasbruce I think we are talking at cross purposes. I was writing about the changes so far. Assignment has not been changed. I have not tried it, so cannot say what it does to productivity (it's probably not that important either way). We still should give it a serious discussion, and I have stated the arguments in favor. But if the consensus is that = is preferable, I won't insist.
def incr = x = x + 1
Has anyone actually seen code like this in the wild ? I have been programming for close to 30 years and have never seen anything like it. It's deliberately bad code and generally that developer wouldn't be lasting long at the company.
So why are we making large design decisions on such a weak use case ?
@odersky
The problem is what to do with dereferencing.
Good point. I am not saying it's the best solution, but C++ has a limited solution for "dereferencing" defined by the target type and even Javascript has a simple solution for primitive types.
@jducoeur
What you say is not exactly wrong, but kind of misses the point:
A purely commercially-focused Scala 3 would involve zero backward incompatible changes, because like you ably point out, rewrites don't help with retraining or updating thousands of books and blog posts and videos.
This is why you can still compile pretty much any Java 1 code with the latest Java compiler.
Instead of being focused on designing a better language, such an industry-focused Scala 3 would be focused exclusively on pain points experienced by existing commercial users (compilation speed, async, etc.), without regard for potential new markets and new audiences.
Scala 3 is not that language—anyone can tell from a cursory reading of Scala 3 documentation that it is first and foremost trying to be a much improved version of Scala 2. Folks are free to disagree with that goal but that is the manifestly the goal of the language, and you are absolutely correct that this goal _will_ cause short-term disruption. But whether assignment is changed to := will not have any significant effect on that short-term disruption, given the scope of changes at play.
Essentially, there was a case to be made many years ago that Scala 3 should be commercially-focused, but the time for that case has long past, and it is better to go "all-in" on some goal than risk a major language version that fails to focus exclusively on pain points of existing commercial users, and also fails to produce a significantly better version of the language.
I would like to echo @jducoeur 's comments, which exactly hit the nail on the head. There are so many changes being made, so quickly, and with such lack of justification, or sympathy for anyone not wanting pointless churn solely to achieve a subjective idea of language regularity. This language is now being directed as if it were a toy pet project rather than a major tool that tens of thousands of companies and developers depend upon to do work and put bread on their tables. With every additional wide-reaching change like this, the outlook for Scala looks dimmer and dimmer as you signal to your users that you do not care about their financial and career investments. I really wish that the silent but vast majority of Scala users who code to use it as a tool rather than an end in itself, would engage more in these discussions.
Please do not make this change.
Has anyone actually seen code like this in the wild ?
Yes, in scala-swing for example. The fact is, though, that in many many cases you go for immutable structures, and so variable assignments are not massive in an average Scala codebase IMO. Which also implies that the fear that massive amounts of code will have to be adjusted doesn't hold. In actuality you don't use variable assignments that much.
I like this idea (but then Pascal was the second language I learned after Basic :), but on the other hand, I'm not so convinced that added symbols/characters are nice when you work with custom containers (as jdegoes mentioned); here you already have to add parenthesis on the LHS, like
trait Ref[A] { def apply(): A; def update(x: A): Unit }
def r: Ref[Int]
r() = r() + 1
This looks better than
r() := r() + 1
But maybe I'm wrong, one would have to test it on an actual code base.
A purely commercially-focused Scala 3 would involve zero backward incompatible changes, because like you ably point out, rewrites don't help with retraining or updating thousands of books and blog posts and videos.
Speaking from those commercial trenches, this is an exaggeration -- it's an extreme strawman that doesn't match reality. Folks aren't all that afraid of gradual, steady improvements -- indeed, many folks here are quite excited by the idea of Scala 3, and hungering for many of its features.
But gradual evolution is not the same thing as a big-bang "change the entire world and retrain hundreds of staff in loads of different changes all at once". That's a much harder sell -- not too far off from switching to another language entirely -- and risks leaving people angry with the Scala community for doing something so irresponsible. It's exactly the sort of thing that does divide communities.
I'm coming at this as somebody who runs a Scala Meetup, helps run a Scala conference, tutors and trains new Scala engineers regularly, as well as slamming out production Scala code. So yeah, I care about the commercial viewpoint, but I care even more about the community. And I sincerely believe that, from the community's POV, trying to do too much at once is deeply unwise.
Dotty had deep community support a few months ago (I've been evangelizing it heavily for 14 months now), but at this point there are a lot of us feeling like too much is being shoved down our throats, rather aggressively and at the last minute, brushing off much of the community feedback. I don't think that's a good way to make for a successful transition...
Why dont just not write like this
def incr = x = x + 1
And use good practice things like
def incr() = x = x + 1
or
def incr(): Unit = x = x + 1
or
def incr =
x = x + 1
or
def incr(): Unit = {x = x + 1}
It will fix the problem and there is no need to use :=
IMO both def incr = x = x + 1 and def incr := x = x + 1 looks bad.
A purely commercially-focused Scala 3 would involve zero backward incompatible changes
This is not true, businesses evolve. Not all businesses have the same needs, some prefer to stay with Java, others like what Scala has to offer. Choosing a technology goes much futher than the technical aspects, like expectations in stability, evolution, community, long-term risks. A company that picks up Scala knows that it doesn't offer eternal backwards compatibility. If Scala 3 was all about zero backward incompatible changes, we would also break expectations of current Scala users.
Scala 3 is not that language—anyone can tell from a cursory reading of Scala 3 documentation that it is first and foremost trying to be a much improved version of Scala 2.
While this is also "not exactly wrong", I know for a fact that backwards compatibility is a fundamental principle behind each and every discussion and decision that goes into Scala 3. Optional braces is a good example: the feature - if accepted - will be really that: optional braces, which one can use if it makes the code more readable. Too breaking changes were discarded, like https://github.com/lampepfl/dotty/issues/7136. If Scala 3 was all about being the fresh-plate new language, it would break a lot more expectations of current users than it does. The fact that there are some breaking changes doesn't justify introducing other breaking changes - every change has to stand on its own.
@jducoeur
Dotty had deep community support a few months ago (I've been evangelizing it heavily for 14 months now), but at this point there are a lot of us feeling like too much is being shoved down our throats, rather aggressively and at the last minute, brushing off much of the community feedback.
I definitely hear what you're saying. Some people are upset by some recent proposals, and want to have more input on the direction of the language.
In order to get the support of the people who are most upset by the latest changes, I believe it would be necessary to rewind to a pre-given state, i.e. Scala 2.x syntax with the new Dotty type system and deletion / deprecation of some fringe constructs (e.g. forSome). That's a lot of language evolution out the window, or at least placed on the back-burner for now.
I'll present a perspective that people seldom talk about: which is that being as it is a disruptive evolution of the language, it is unattractive to many audiences to release 3.0 with significant revisions of the language planned for future revisions. It adds enormous risk and uncertainty.
It discourages library authors from creating libraries, discourages book authors from writing books, discourages instructors from creating courses, discourages companies from jumping to 3.0 until things stabilize. A future in which Scala 3.0 is pre-given, and Scala 3.1 introduces given, and Scala 3.2 deletes implicits, Scala 3.3 adds whitespace sensitive syntax, etc., is not an attractive future for any of these audiences.
Bottom line: if you're in the market to create _new_ materials, _new_ libraries, and _new_ companies using Scala 3.0, you want to be pretty damn sure it's going to be stable, so that your investment is not immediately made obsolete. Pushing significant language changes further out, while making it easier for some to sell Scala to their existing companies (a laudable goal!), has a profound impact on the willingness of creators to build out a new future for Scala.
There are other issues to consider as well, such as the current rate of churn (Scala is experiencing noticeable churn, with many companies unable to hire Scala talent, and moving to Java or in a few cases Kotlin for new and existing projects). Not all of the current markets for Scala are the most interesting ones, and it's conceivable (if not guaranteed) that language evolution can open Scala to new markets or decrease churn in some existing market segments.
All this to say, the big picture here is complex, and while existing users may be justifiably upset by some proposed language changes, this is similar to what happens when a company significantly updates a product (Twitter, Facebook, etc.): it's going to make some users long for the old product, and experience transition pain, but may end up being the best thing for the product's future.
I'm not saying that's a "given" (it's not!), but it's at least a possibility, and it's my stated view that even though there exists an alternate future in which Scala 3 is entirely industry focused (and doesn't significantly improve on the language, at least in any backward-incompatible way), the fork to that future happened quite a long time ago, and our best chances of success lie in a new unified vision of Scala 3 that represents a significantly better version of the Scala language.
For background: note I've founded 2 companies that used Scala, poured $13m into Scala startups, organize several annual conferences and many hackathons that feature Scala, teach thousands of Scala developers, and know many Scala developers at every major employer of Scala developers. So I clearly have a lot at stake in the future of Scala, like yourself and everyone else here. At the end of the day, I have no doubt we all want what is best for Scala's future, and it's natural that not everyone is going to have exactly the same view on how to get there.
IMO both
def incr = x = x + 1anddef incr := x = x + 1looks bad.
That's supposed to be def incr = x := x+ 1, which I think is clearer.
Are we distinguishing between assignment and name binding here? If we have
def f = ...
then do we keep
val x = ...
since you said
But in Scala, we do use = for definitions
So we'd have
val x = ...
var y := ...
which I think would be fine, since most people are trying to avoid vars anyway, and making mutation stand out isn't a bad thing. But if you're proposing
val x := ...
too, then that seems inconsistent to me.
I use scala primarily as a tool. Most of my time is spent doing ML and data things. I certainly only represent one small user segment and I currently don’t need to worry about a big team (bliss for the moment).
There is a lot of change in languages everywhere. For other parts of my work I code in typescript, python, javascript, and based on some google changes, Swift for ML in the future. Major algorithms and frameworks are also constantly, rapidly changing.
At the moment, these languages are undergoing change, often monthly, often for the better. It’s a pain to look up the changes monthly but the benefit is often worth the thrash.
From my perspective, scala 3 is not changing that fast. I’m happy to try something if there is a realistic chance at a productivity boost. There are a lot of interesting points of view around these changes and how these changes come about but the fact that there are changes adds very little to the sea of changes out there.
Considering that there were not a lot of scala changes for a long time, I’m happy to give a PR a shot especially if the scala change cycle slows down once scala 3 deploys.
Different operators for assignment vs definition seems worth a serious consideration as @odersky states. If I'm understanding correctly, this only impacts vars, i.e. mutating state. Is this correct?
// var definition plus assignment
var x := 10
//var assignment
x := 11
//function that assigns to a var
def inc = x := x + 1
// all immutable code stays the same??
val y = 20
def nexty = y + 1
Most Scala code that I've dealt with consists of immutable logic, with mutation reserved for rare performance optimizations. So, maybe this is not be a huge impact in real world functional code? If the change makes parsing simpler, maybe it's a net plus. On the other hand.... don't a lot of Scala DSLs (sbt) use the := operator already?
If I'm understanding correctly, this only impacts vars, i.e. mutating state. Is this correct?
Correct.
On the other hand.... don't a lot of Scala DSLs (sbt) use the := operator already?
That's actually a plus, since := in these libraries means assignment. So bringing primitive assignment in line would make things more uniform.
A couple of people have referred to := as an operator. As explained on page 29 (47) of https://jmvdveer.home.xs4all.nl/learning-algol-68-genie.pdf, in Algol 68, := is not an operator:

Is this distinction relevant for our purposes?
I don't like that when reading a line in isolation x := y, I cannot know whether that is assignment or method call without knowing the full type definition of x. Even then, x can have a type without any method :=, but with an implicit conversion in scope that tacks on a := method. I believe it's more consistent to not allow overloading of the assignment operator.
I agree, but that should be applied to vals and:
val foo := 3
is more verbose than just
foo := 3
And
val foo: Int := 3
looks really weird.
My favourite syntax is
foo Int := 3
, or even
val foo Int := 3
It's true that the duplicate period when declaring the type of a variable looks strange
var foo: Int := 1
or as ascription
var foo := 1: Int
? Perhaps then a softer solution of only using := for _re_-assignment is better.
Or it's an indicator that period denoting type ascription is problematic to reuse in general? Looking at the language comparison table, perhaps <- wasn't such a bad suggestion.
def incr() = x <- x + 1
def incr() =
x <- x + 1
var foo: Int <- 3
var foo <- 3: Int
:confused:
It's true that the duplicate period when declaring the type of a variable looks strange
var foo: Int := 1or as ascription
var foo := 1: Int? Perhaps then a softer solution of only using
:=for _re_-assignment is better.
Another possibility:
: Int
foo := 3
Lets remove var as a language feature altogether and pursue the idea explained by John de Goes few posts above (https://github.com/lampepfl/dotty/issues/7598#issuecomment-557465708).
I believe solving the "dereferencing" problem is easier and much more beneficial than agreeing on the syntax change.
If we make var x = 1 a syntax sugar for val x: Var[Int] = 1 all the problems go away. There is no magic in := anymore, its just an operator on Var (conceptually at least). The one thing I loved about scala from the very beginning was minimalistic syntax/grammar and expressing stuff in user space based on few highly expressive features instead of the tailored, compiler-space additions.
For me programming languages are foremost about the mindset behind their design. Removing vars as first lass citizens (and demoting them into syntax sugar) would send a clear message about what ideas stand behind Scala. It's similar to the message sent by "vals by default".
I'm very much in favor of this change. Reassignments are used rarely and almost purely for performance purposes. They are unsafe and bugprone. Lets make them visually distinctive and less natural to use.
1970 - Niklaus Wirth creates Pascal, a procedural language. Critics immediately denounce Pascal because it uses "x := x + y" syntax instead of the more familiar C-like "x = x + y". This criticism happens in spite of the fact that C has not yet been invented.
James Iry, A Brief, Incomplete, and Mostly Wrong History of Programming Languages
I am fond of Pascal. It is a virtue of Scala to bring back arbitrarily nested function and variable declarations. That is why I like switching to := instead of = for assignments. Good syntax should, without IDEs, tell apart a let-bindings from state-changing instruction. Of course, there is concern about the disruption form this change. Comments above have sketched a few _dramatis personae_ to this conversation and their stakes, with which one can empathise.
However, since qualitative discussions are too vague, perhaps we should add some numbers. Let us, say, pick up some salient open-source Scala projects, such as Spark, Akka, Play, Cats, Monix, etc... and count how many lines of code would be changed by this change. That, and perhaps some measures by those in private companies, should tell us how much disruption to expect.
My bet is on the compiler and standard library topping the ranking.
I'm in favour of making assignment look different from definition.
But I don't think we should use :=. It is used a lot in DSLs and doing so will break a lot of existing libraries.
I suggest using #= for assignment, and deprecate/ban #= from being user-definable.
#= is used a lot less in libraries AFAIK# is not an operator people associate with numbers#=, I will know that a binding is being reassigned and not that the container type is potentially doing some internal mutation. This also avoids the situation where a library author adds a new #= operator/method and breaks existing code:=, I know it's 100% it's library code+=, -= and friends are ok to be library functions I think, because it HAS to be provided by the library author in order for my code to compile, unlike assignment.I like this proposal.
Now Dotty starts to look like a language I wish Scala had been from the very beginning.
The problem that many seem to point out is that Dotty is not Scala anymore.
Perhaps it shouldn't be Scala3, and it should stay Dotty, a language compatible with Scala2, but a different language per se.
Then you could do even more radical changes without accusations of changing Scala too much!
I'd remove compound operators completely :)
Is it completely loco to ask that the := notation be also used to identify functions and methods that have side-effects? Purely functional libraries have to assume the developer will exercise discipline and never launch the missiles in a "pure" function. If there was a way for the compiler to verify that A => B is pure and, say, A :=> B isn't, then that assumption can be enforced by library authors.
instead of := why not use reverse arrow like
val a <- 10
var b <- "test"
which will be inline with the for comprehension
OR
for functions instead of using equals we could use the arrow(or fat arrow) symbol, example
def test(someParam : Type) : SomeOtherType -> {
....
}
then the inc example could be like
def incr -> x = x + 1
my 2 cents
Hum but then how do you actually assign something to a var whose type has a := method?
You mean a
:=method that is applicable to the right hand side, i.e. to the type of the variable itself? That looks like a pretty extreme and nonsensical case. If you do that then maybe you should not be able to assign to such a var.
I just realized that I actually do have code like that, existing today in my codebase:
https://github.com/scala-js/scala-js/blob/67953468f1a4c1de5630cc545af555dad5a0b155/linker/shared/src/main/scala/org/scalajs/linker/backend/emitter/ClassEmitter.scala#L810
I am reassigning a var of type js.Tree with an rhs of type js.Tree. But js.Tree has a an (extension) method := taking a js.Tree as argument (it creates a js.Assign node).
So I very much disagree that it is an "extreme and nonsensical" case.
With the proposal here, I wouldn't be able to write this code at all!
I'll present a perspective that people seldom talk about: which is that being as it is a disruptive evolution of the language, it is unattractive to many audiences to release 3.0 with significant revisions of the language planned for future revisions. It adds enormous risk and uncertainty.
It discourages library authors from creating libraries, discourages book authors from writing books, discourages instructors from creating courses, discourages companies from jumping to 3.0 until things stabilize. A future in which Scala 3.0 is pre-
given, and Scala 3.1 introducesgiven, and Scala 3.2 deletes implicits, Scala 3.3 adds whitespace sensitive syntax, etc., is not an attractive future for any of these audiences.Bottom line: if you're in the market to create _new_ materials, _new_ libraries, and _new_ companies using Scala 3.0, you want to be pretty damn sure it's going to be stable, so that your investment is not immediately made obsolete. Pushing significant language changes further out, while making it easier for some to sell Scala to their existing companies (a laudable goal!), has a profound impact on the willingness of creators to build out a new future for Scala.
@jdegoes my problem with this is that I am very very skeptical that the plan of stopping to evolve the language will happen. Software never works that way. There is always change. If we don't embrace that fact and leverage it I don't think we will be better off.
So I very much disagree that it is an "extreme and nonsensical" case.
I agree it's sensical, but it's still an outlier. This is like saying that we want to use the word class in a meta-programming framework as the name of a structure that represents a class. Well, we can't and we are all used to that. We tend to write clazz or cls or something similar instead. The use case for := is pretty much the same. If := is assignment, then we are well advised to call our meta-operation something else (dotc uses becomes for exactly the same creation method).
The thing is, we did call our meta operation something else: the entire
community has settled on :=! In scala, := is the worldwide standard for the meta operator for assignment
We did exactly what you are now suggesting
we should do, and still face our code being broken after the fact. That makes the “choose something else” argument lose all credibility. This wouldn’t be the case for someone starting an ecosystem from scratch, but we’re a decade past that point by now
Scalatags’ := behaves exactly the same as @sjrds, and there is a lot of Scalatags code in the wild, in addition to all its successors (every HTML templating library since 2014). Switching it from:=tobecomes` would totally destroy the API they are trying to present as a HTML templating library
Can you show an example how code would end up being broken? The type of := in scalatags is too complex for me to be sure what it can do.
I am a bit tired discussing this further, since it is such a sideshow. Yes, if there are reasonable examples where indeed primitive assignment cannot be expressed, one should invent an escape hatch. Let's see how this works out first. In any case we'd have ample time to find out since = will not go away anytime soon.
The thing is, we did call our meta operation something else: the entire community has settled on
:=! In scala
Well, the entire community? There is #= used in the Scala bindings to JaCoP, there is apply and update used in scala-stm, etc. I think there are several ideas of how to "virtualize" =.
At this point, I feel the development of Scala is going for the wrong direction. It is trying to turn into a subjectively perfect language, rather than serving the community better and getting more adoptions. I can foresee very little adoption in the companies and gradually moving away from Scala overall.
The type of := in scalatags is too complex for me to be sure what it can do.
Once you peel back all the abstract types, and typeclasses, it's basically
class Attr(key: String, value: Option[String] = None){
def :=(value: String): Attr
}
The point of this is so you can define attributes for use in tags,
val width = new Attr("width")
div(
width := "500px"
)
But some attributes have a default value, so they can be defined with their value up-front and used "bare":
val disabled = new Attr("disabled") := "disabled"
// Both of these are ok, and match the semantics of optional attribute values in HTML
div(
disabled := "disabled"
)
div(
disabled
)
And it's also no uncommon to pass the := expressions around, in user code, for use later:
val modifier = width := "500px"
div(modifier)
Apart from trivial examples like this, typically you'd put them in collections, aggregate them in Arrays or ArrayBuffers, possibly doing normal mutable-arraybuffer things to the collection, before finally applying them all to some HTML tag like div() to generate output.
Essentially, a := b is an expression that returns the same type as a (Attr), and they're used relatively frequently as normal values that we pass around. They're used in vars or mutable collections about as often as any other code: not all the time, but neither is it unheard of. It would be odd to say that if you assign a value of this type to a var or mutable collection, you could no longer use := to associate it with a value.
And becomes looks horrendous in this context:
div(
div(id := "ch1Panel", role := "tabpanel", aria.labelledby := "ch1Tab")(
"Chapter 1 content goes here"
),
div(id := "ch2Panel", role := "tabpanel", aria.labelledby := "ch2Tab")(
"Chapter 2 content goes here"
),
div(id := "quizPanel", role := "tabpanel", aria.labelledby := "quizTab")(
"Quiz content goes here"
)
)
// vs
div(
div(id.becomes("ch1Panel"), role.becomes("tabpanel"), aria.labelledby.becomes("ch1Tab"))(
"Chapter 1 content goes here"
),
div(id.becomes("ch2Panel"), role.becomes("tabpanel"), aria.labelledby.becomes("ch2Tab"))(
"Chapter 2 content goes here"
),
div(id.becomes("quizPanel"), role.becomes("tabpanel"), aria.labelledby.becomes("quizTab"))(
"Quiz content goes here"
)
)
Wouldn't it be easier to just let := be assignment to var if it's a var, and if you want to use the := method or enriched method you have to backtick-escape it?
var width = new Attr("width")
width `:=` "500px" // why did I make that a var?
Wouldn't it be easier to just let := be assignment to var if it's a var, and if you want to use the := method or enriched method you have to backtick-escape it?
Yes it's possible; I'm not arguing that using := for assignment is a terrible idea altogether. I'm just insisting that these :=-using APIs are not outliers, we cannot dismiss code using it as "oh you should have used something else", and if we want to do this we must have an escape hatch.
Using backticks to escape and call a method sounds fine on its own, but there's a somewhat jarring incongruity with the handling of += (and similar methods) where the method takes priority, and you need to use the escape hatch if you want to use the re-assign local variable semantic:
@ var x = collection.mutable.Set(1)
x: collection.mutable.Set[Int] = HashSet(1)
@ x += 2
res22: collection.mutable.Set[Int] = HashSet(1, 2)
@ x
res23: collection.mutable.Set[Int] = HashSet(1, 2)
@ val y = x + 3
y: collection.mutable.Set[Int] = HashSet(1, 2, 3)
@ x
res25: collection.mutable.Set[Int] = HashSet(1, 2)
Given how closely the := method/assignment split mirrors the += method/assignment split, it would be nice if we could somehow harmonize them. I don't yet know if that is feasible given current constraints
Thanks for explaining! This use case does not present a problem. Here's what you would get:
var x, y: Attr
x := "abc" // calls the method
x := y // is a primitive assignment
@sjrd's use case is different: there, the argument to := is the same as the receiver and the result type.
class Tree
def := (t: Tree): Tree
In this case, the := method permanently shadows the primitive assignment. @sjrd's use case is a true meta operation := _stands for itself_ at the level of representations. My remark was that for this very particular use case I think it would be OK to pick an other name, such as becomes. For general usage of := for assignment-like operations in scalalags, sbt, Chisel, etc, of course we want to keep using :=. It would be crazy to disallow that.
Some people very upset about this: https://www.reddit.com/r/scala/comments/e03pve/use_for_assignment_scala_3/
You can dismiss them as Reddit trolls or whatever but the real question is once this is released how much bad press it will get. Scala has suffered enough from bad press, I think we should be very wary of risking more of it.
@odersky One idea I haven't seen mentioned: if we just want to avoid ugliness in this case
def incr = x = x + 1
Why not we do this change instead:
Assignment x = x + 1 is no longer an expression; instead it is a statement, and must always appear wrapped in {curly brackets}
This would force a user to write
def incr = { x = x + 1 }
Which I think we can all agree is no real hardship, and possibly already looks like the "best practice" code style of today.
Furthermore, instead of adding an ambiguity conflict by mixing := assignment with := method calls, it would remove an ambiguity conflict between named arguments and assignments
def incr(x: Int, y: Int) = x + y
var x = 2
// before
incr(x = 1, y = 2) // ambiguous (named arg or assignment?)
// after
incr({x = 1}, y = 2) // Only way of writing assignment now
incr(x = 1, y = 2) // Only can mean a named arg now
This way, we get no ugly x = x = x + 1 strings in our code, enforcing what is already today's best practice code style, with a side effect of remove one existing case of syntactic ambiguity, while introducing no ambiguity of our own. It also will be of minimal migration pain, from a human point of view, since we can already write/read code like that today and it does exactly what we expect (though programmatically we will need to fix up any use sites to avoid compile errors)
Seems like a strict improvement over both this proposal, as well as the status quo
We may want to broaden it slightly to allow usage immediately in the body of if-else, while and other control structures, for compatibility with C-style syntax, or we might now. That's a detail that can be discussed separately
I've been thinking about this proposal for a few days now and the more I think about it the more I like it. It improves clarity by distinguishing definition and assignment (the def incr = x = x + 1 example is truly an abomination) and it imposes a syntactic penalty for mutation. I have long been bothered by the fact that var and val are the same length and differ by just a single character. The visual distinction between := and = is much more obvious that var vs val.
At a glance, I don't like the new proposal by @lihaoyi directly above this comment because it seems to only address the def incr ugliness and neither addresses the ambiguity of definition vs assignment nor imposes a syntactic penalty for mutation.
It's unfortunate that a good amount of the discussion in this thread (and in other channels) has been rather reactionary and not really about the merits or demerits of the proposal. Regardless of whether or not this proposal is accepted, I find it difficult to imagine that the success of scala 3 is contingent on this minor (though still quite beneficial in my view) syntactic change. I also find the notion that new and existing developers will struggle with learning := collectively insulting to their intelligence.
The points that Heather Miller made in her scale by the bay keynote are relevant to the current scala 3 hysteria. The software development field is growing so quickly that newcomers will outnumber the old heads in the blink of an eye. If 70% of scala 2 users abandoned scala due to the scala 3 changes, scala 3 could still be an enormous success if it spurred growth in new areas. The growth trends for scala 2 do not seem great so it seems foolish to bend over backwards to accommodate all of the legacy use cases. For all the handwringing about the python 2/3 split, last time I checked it was both one of the most popular languages and fastest growing. Right now, scala can't really make either claim.
I don't think this is a good way to gatekeep discussion. Most of us do not have the ability to use an experimental fork of a niche language full time, nevermind for a couple of months! After all, we already have full time jobs, and million-line Scala codebases that we're not going to port to dotty in the next week.
This requirement basically limits the discussion to those who work full time at EPFL on Dotty, locks everyone else out, and dismisses years of industry experience as "stockholme syndrome". Not exactly an inclusive, collaborative, or diverse basis to begin a discussion; likely you'll find the only input that passes that bar is your own.
What would you propose as an alternative to the dotty status quo for getting feedback on new language syntax? I have read a number of blog posts about experimenting with dotty which imply that a number of non-epfl people (including people working in industry as well as hobbyists) have been tinkering with dotty. It would certainly be foolish to disregard feedback from anyone out of hand -- and I doubt that @odersky literally meant that you need to use dotty for months to offer feedback even if the stockholm syndrome comment was a bit alienating -- but he has a point that often you do have to try something for a while to properly evaluate it. I've learned this is the case with restaurants just as much as programming. I fear that if progress in scala is dictated by industry and reddit threads, it will pander to the lowest common denominator.
What would you propose as an alternative to the dotty status quo for getting feedback on new language syntax?
Respect for people's experience, history of contributions, demonstrated competence, and knowing what they want is a reasonable alternative. People here have years-to-decades long careers working in dozens of different languages and environments, many of which have syntax similar to that being proposed. We're not a class of 1st year undergraduates.
Imagine if you were a salesperson trying to hawk some SAAS software, and your sales pitch was "you have to try it for months, we promise you that you'll grow to like it". They'd think you're crazy, and for good reason, because you probably are.
The whole point of a design document/proposal/discussion process is to build consensus without everyone having to spend large amounts of time becoming invested in something. If the document is "here it is, ready or not" and the discussion is "try it for a few weeks/months, I think you'll like it", the entire process has failed. Such a design document/proposal would never pass muster in any professional environment.
Respect for people's experience, history of contributions, demonstrated competence, and knowing what they want is a reasonable alternative. People here have years-to-decades long careers working in dozens of different languages and environments, many of which have syntax similar to that being proposed. We're not a class of 1st year undergraduates.
Imagine if you were a salesperson trying to hawk some SAAS software, and your sales pitch was "you have to try it for months, we promise you that you'll grow to like it". They'd think you're crazy, and for good reason, because you probably are.
Wow. That was a very respectful response! Thank you.
Ethan, he wasn't talking about you! https://www.dictionary.com/browse/you
I agree with Li Haoyi. Furthermore, even if 5 or 20 or 100 people do feel
more productive after using it for 3 months, that doesn't mean there can't
be valid reasons not to ship something in 3.0. (1) Feeling more productive
can be subjective. So disregarding people's input is never valid. (That
doesn't mean everyone has veto power, but every argument has to be
considered.) It might make other people less productive. (2) Even if it
only makes people more productive, you also have to take into account the
PR effect changes have. If the result is going to be a spate of rants on
dzone and reddit about how scala has gone off the rails, it's not going to
help with scala's adoption and perception issues.
On Sun, Nov 24, 2019 at 8:25 PM Ethan Atkins notifications@github.com
wrote:
Respect for people's experience, history of contributions, demonstrated
competence, and knowing what they want is a reasonable alternative. People
here have years-to-decades long careers working in dozens of different
languages and environments, many of which have syntax similar to that being
proposed. We're not a class of 1st year undergraduates.Imagine if you were a salesperson trying to hawk some SAAS software, and
your sales pitch was "you have to try it for months, we promise you that
you'll grow to like it". They'd think you're crazy, and for good reason,
because you probably are.Wow. That was a very respectful response! Thank you.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/lampepfl/dotty/issues/7598?email_source=notifications&email_token=AAAYAUDXLTMJDRZDAEMCUCLQVMSQFA5CNFSM4JQA26SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFA26CA#issuecomment-557952776,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAAYAUF2IJZNI57A34FYAUTQVMSQFANCNFSM4JQA26SA
.
I am sorry if the remark about Stockholm syndrome irked people - it was meant as a figure of speech. I believe my history of interactions has shown that I do value and take into account feedback everywhere. Otherwise I would not spend considerable time engaging in this discussions! And, yes, there are a number of dimensions that need to be brought up, some of them technical, the others issues of perception. On the technical and UX issues I tend to give more weight to the opinions of people who have actually tried the stuff.
What I tried to convey in my comment that seemingly went over badly is my enthusiasm for what a great language we have achieved! It's a really pleasing experience to write serious code in Scala 3. I would never have thought that the combination of small syntax changes would have such a dramatic effect on usability. And in my opinion that aspect has to be experienced, you can't give it justice if you have not tried it.
While I can see the value in making mutation stand out more and be set apart from definition, on a syntactical level, I fear that the overal cost/benefit ratio of the proposed change is negative.
Consider these two definitions:
val x = 1
var y = 1
While from a more higher-level, functional perspective these two things might be two entirely different things they compile down to very same byte code: Writing a value into to a "named" memory location. To people coming to Scala from more imperative Languages the difference between x and y only manifests itself in the things that can be done to x and y, specifically that y can be "changed".
The _initialization_ of x and y however is exactly the same operation, down to the machine level.
Therefore, from in imperative viewpoint, I'd say the snippet above captures the essence of the two definitions perfectly.
This, however, appears less principled:
val x = 1
var y := 1
The difference between x and y is already expressed via the val vs. var keywords. The initialization operation itself is identical but now requires two different syntaxes. When seen on a low level there is only _one_ difference between x and y, namely that y can receive a different value later vs. x can't, and this difference doesn't even manifest itself on these lines themselves but only on the lines _using_ these definitions.
Yet, we now have to change val x = 1 in _two_ places to turn it into a true "variable" and it now looks as if the initialization code were somehow different between the two lines.
I fear that this would confuse people coming to Scala from imperative languages, while the first snippet is perfectly clear.
So, the benefit to new Scala users could be negative, especially at the very beginning.
Is this disadvantage then overcompensated by benefits reaped later when people have more experience and work in more idiomatic Scala codebases?
I'd say no.
This change would affect an area of the language that, while I'd consider it a crucially important part, is rightfully used less and less the more experience one gains with the language. Many idiomatic Scala codebases hardly use any vars at all.
So any improvements to their syntax won't really make any difference to them.
vars are simply too rare.
Therefore we are faced with a proposal that has a high risk of being unhelpful to newcomers while at the same time provides little benefit to the already large, existing user base. Especially when viewed under the principle that changes to the language must present a relatively large improvement over the status quo in order to offset the considerable cost of the change itself, I'd say that the best way forward would be to leave things as is.
IMO forcing brackets is much more acceptable than replacing = with := for assignment, and
def incr = { x = x + 1 }
looks okay to me. This is also the case for forcing indentation.
Agreed. Even if assignment syntax is changed for some reason to :=, a var
should still be defined with =. There is a very elegant symmetry in val,
var, and def having the same syntax, after all they're the same thing,
assigning a name to an expression, except for different semantics about the
specifics of that, and because of def's semantics it can also have
parameters.
If you like, call := the reassignment operator.
(I still don't think the change is motivated enough to justify it, but if
it is, I would still argue the above.)
On Mon, Nov 25, 2019 at 5:53 AM Mathias notifications@github.com wrote:
While I can see the value in making mutation stand out more and be set
apart from definition, on a syntactical level, I fear that the overal
cost/benefit ratio of the proposed change is negative.Consider these two definitions:
val x = 1var y = 1
While from a more higher-level, functional perspective these two things
might be two entirely different things they compile down to very same byte
code: Writing a value into to a "named" memory location. To people coming
to Scala from more imperative Languages the difference between x and y
only manifests itself in the things that can be done to x and y,
specifically that y can be "changed".
The initialization of x and y however is exactly the same operation,
down to the machine level.
Therefore, from in imperative viewpoint, I'd say the snippet above
captures the essence of the two definitions perfectly.This, however, appears less principled:
val x = 1var y := 1
The difference between x and y is already expressed via the val vs. var
keywords. The initialization operation itself is identical but now requires
two different syntaxes. When seen on a low level there is only one
difference between x and y, namely that y can receive a different value
later vs. x can't, and this difference doesn't even manifest itself on
these lines themselves but only on the lines using these definitions.
Yet, we now have to change val x = 1 in two places to turn it into a
true "variable" and it now looks as if the initialization code were somehow
different between the two lines.I fear that this would confuse people coming to Scala from imperative
languages, while the first snippet is perfectly clear.
So, the benefit to new Scala users could be negative, especially at the
very beginning.Is this disadvantage then overcompensated by benefits reaped later when
people have more experience and work in more idiomatic Scala codebases?
I'd say no.
This change would affect an area of the language that, while I'd consider
it a crucially important part, is rightfully used less and less the more
experience one gains with the language. Many idiomatic Scala codebases
hardly use any vars at all.
So any improvements to their syntax won't really make any difference to
them.
vars are simply too rare.Therefore we are faced with a proposal that has a high risk of being
unhelpful to newcomers while at the same time provides little benefit to
the already large, existing user base. Especially when viewed under the
principle that changes to the language must present a relatively large
improvement over the status quo in order to offset the considerable cost of
the change itself, I'd say that the best way forward would be to leave
things as is.—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/lampepfl/dotty/issues/7598?email_source=notifications&email_token=AAAYAUD2JHVH42J5ESMRMQLQVOVBLA5CNFSM4JQA26SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFB7GQQ#issuecomment-558101314,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAAYAUE3G3YN7P4H56OPTT3QVOVBLANCNFSM4JQA26SA
.
Migration: Scala 3.0 should allow = and :=. = would be deprecated at some later version. This will probably take some time.
I have now read lines similar to this one in many proposals. Regardless of the proposal, every time I read a line like that it scares me a little bit more.
I realize that it's necessary to be able to compile as much Scala2 code with Scala3 as possible. But by the time Scala 3.0 gets released it will contain so many new things—_and_ still support all the old things they are supposed to replace—that all the permutations of ways to write the same piece of code will have exploded exponentially. Am I the only one who fears that this might cause Scala 3 to be viewed by some as a huge pile of unrelated syntax and features, while the goal was actually to make things simpler and reduce the amount of ways to do the same thing?
Even if everyone was disciplined enough (then why is the simplification even necessary?) to only write new code with the new syntax, you'd still get codebases that are a mix of new things and old things, and new developers would still need to learn all the old stuff too, including how all the old stuff interacts with the new stuff.
If you need a graceful migration, then you need a period in which both are supported. Otherwise, you can't change things.
I would never have thought that the combination of small syntax changes would have such a dramatic effect on usability. And in my opinion that aspect has to be experienced, you can't give it justice if you have not tried it.
Most of us have tried the various features in other contexts though; we're not only writing Scala!
Many have experienced indentation-delimited blocks writing Python. It's the most popular language in the world after all. Some people love it, some people love it enough to have ported it to Scala 5 years ago, and yet others hate it with a burning passion.
Many have experienced various languages using :=, and various languages using different delimiters for variable initialization and update: Go uses := and =, F# uses = and <-. It's really not that new an idea.
There's also the flip side: that many are experiencing things that someone full time working on Dotty and teaching undergraduates using Scala might not be experiencing:
Large codebases stuck on an endless upgrade treadmill, 24/7 365 days a year upgrading between already-old versions of the language, where compatibility flags are a lifesaver in allowing you to upgrade, even as they reduce the "purity" of the language spec.
Teams who are not Scala enthusiasts, couldn't care less about language syntax, and value non-breaking improvements that make their lives better at no cost
Maintaining large open source Scala libraries and ecosystems, using many Scala language features, and exercising Scala in a wide variety of fields and domains that are not "we are writing a Scala compiler" or "Programming 101"
it's entirely possible that a change massively benefits someone writing Scala compilers and teaching undergraduates, and still have the change do great harm to someone running the upgrade treadmill on an old project, or selling Scala to a non-language-enthusiast team. It's entirely possible that a change makes Scala look great to a Python enthusiast, while making it look horrible to a Python un-enthusiast. There is no contradiction here.
Which is why I keep arguing against "try it, maybe you'll like it". It's a useless piece of information. Even if I quit my job tomorrow and spent the next 3 months and spent 40 hours a week using a new feature on some project, it would tell me exactly nothing about how well that feature plays in a large professional codebase. It would tell me nothing about how the feature will play in the maintenance of my suite of open source libraries.
In many of these contexts, I can already predict how a change would play out; after all, one role of a software developer is to help predict the effect of a change before investing days/weeks/years in executing on it. It's literally our job!
And that's the purpose of of a proper design proposal/document/review: to get everyone's input, from all their different backgrounds, their predictions on how a change might impact them, without having to perform expensive (and ultimately unhelpful) experiments and investments.
If you prioritize feedback from hands-on Dotty experience, you are prioritizing feedback from people teaching undergraduates, writing Scala compilers, write toy side projects, since those are the only use cases for Dotty right now. It wouldn't be surprising at all if you then end up creating a Scala language optimized for teaching undergraduates, writing Scala compilers, and writing toy projects, at the expense of everything and everyone else. After all, nobody's going to come to you next week and tell you their experience porting their million-line hundred-developer enterprise codebase to Dotty.
If you need a graceful migration, then you need a period in which both are supported. Otherwise, you can't change things.
Sure but my fear was that there comes a point where the resulting language is no longer graceful.
Maybe it looks ugly, but no identifier will ever clash with _=, but at least it aligns with setters
var x = 0
while x < 100 do
x _= x + 1
or perhaps ugly is precisely what we need :)
no identifier will ever clash with
_=
It's not a valid identifier:
trait Ref {
def _= (value: Any): Unit
}
<console>:2: error: identifier expected but '_' found.
def _= (value: Any): Unit
^
You would at least need to use two underscores, like __=. Anyway, I don't see any problem for := if you can force method invocation through backticks.
Sure but my fear was that there comes a point where the resulting language is no longer graceful.
It's a valid fear. In fact, he Dotty repo is a way to combat that: instead of just evaluating language features in a vacuum, it's implemented a number of features, to test how graceful the resulting language functions.
@lihaoyi Just to put some facts straight:
I do a bit more than teaching undergraduates. Through my books and MOOCs I have taught several hundreds of thousands of programmers (total inscriptions have reached 1 Million by now). According to our statistics, 85% of them were professionals (had a first degree).
We also do a bit more than writing compilers. It's also the standard library, and the IDE, and the doc tool, and porting stuff to the Dotty community build (you will see your own libraries in that build shortly). It's true that this is still a fairly special slice of the whole ecosystem; for instance one particularity is that we are sitting at the bottom of the food chain with almost no dependencies. So, yes, other people have other problems which might make them have other priorities.
All I wrote was that in my experience Scala 3 is shaping up to be a great language. You may believe that or not. But there's no need to denigrate the messenger.
Sorry, I do not mean to denigrate anyone. Just explaining that I think “try
it out” is neither necessary nor sufficient to get a good read of things in
many scenarios, and prioritizing trying it out may overfit on use-cases
where trying out experimental tools and changes is easier
On Mon, 25 Nov 2019 at 9:18 AM, odersky notifications@github.com wrote:
@lihaoyi https://github.com/lihaoyi Just to put some facts straight:
-
I do a bit more than teaching undergraduates. Through my books and
MOOCs I have taught several hundreds of thousands of programmers (total
inscriptions have reached 1 Million by now). According to our statistics,
85% of them were professionals (had a first degree).
-We also do a bit more than writing compilers. It's also the standard
library, and the IDE, and the doc tool, and porting stuff to the Dotty
community build (you will see your own libraries in that build shortly).
It's true that this is still a fairly special slice of the whole ecosystem;
for instance one particularity is that we are sitting at the bottom of the
food chain with almost no dependencies. So, yes, other people have other
problems which might make them have other priorities.All I wrote was that in my experience Scala 3 is shaping up to be a great
language. You may believe that or not. But there's no need to denigrate the
messenger.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/lampepfl/dotty/issues/7598?email_source=notifications&email_token=AAHEB7CXRGKGA3ZYQTJUFS3QVQCFRA5CNFSM4JQA26SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFDERKQ#issuecomment-558254250,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAHEB7FTJKPXIEJV4QP7KO3QVQCFRANCNFSM4JQA26SA
.
Guido Van Rossum retired over :=. I decided that I do not want to share the same fate (yet). 😉 I still think changing assignment to := would be the right thing in isolation, but the realities make it messy
x_= as they are; so that would give an inconsistency wrt :=.I have not shied away in the past from proposing to change language features that have a migration cost. But in this case, in the end, the change just does not seem important enough. Maybe syntax highlighting could render assignment equals different from definition equals. That would be a tooling solution to some of the problems.
@lihaoyi Just to put some facts straight:
- I do a bit more than teaching undergraduates. Through my books and MOOCs I have taught several hundreds of thousands of programmers (total inscriptions have reached 1 Million by now). According to our statistics, 85% of them were professionals (had a first degree).
- We also do a bit more than writing compilers. It's also the standard library, and the IDE, and the doc tool, and porting stuff to the Dotty community build (you will see your own libraries in that build shortly). It's true that this is still a fairly special slice of the whole ecosystem; for instance one particularity is that we are sitting at the bottom of the food chain with almost no dependencies. So, yes, other people have other problems which might make them have other priorities.
All I wrote was that in my experience Scala 3 is shaping up to be a great language. You may believe that or not. But there's no need to denigrate the messenger.
I feel like you are taking this the wrong way. I do think these changes are going to make the language better, but better language doesn't mean better adoption or acceptance. If you look at the top language list, they are all horrible languages. But still people use them widely.
Currently Scala is used more than just a teaching or toy language. People use it in the industry, so any change will introduce cost and we are talking real money, and being comfortable to use isn't gonna justify that.
Right but it's an argument in favor of staggering changes over several
releases
On Mon, Nov 25, 2019, 10:26 AM Dale Wijnand notifications@github.com
wrote:
If you need a graceful migration, then you need a period in which both are
supported. Otherwise, you can't change things.—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/lampepfl/dotty/issues/7598?email_source=notifications&email_token=AAAYAUH2EPS5CDWLRE2XMFLQVPVA7A5CNFSM4JQA26SKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEFCYPQI#issuecomment-558204865,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAAYAUDBXBWO5KWY6XFLSATQVPVA7ANCNFSM4JQA26SA
.
@dwijnand reminded quite an important issue
If you need a graceful migration, then you need a period in which both are supported. Otherwise, you can't change things.
If this new syntax for assignment would be introduced gracefully,
x = x + 1 and x := x + 1 are valid and mean exactly the same thingx = x + 1 becomes a deprecatedx := x + 1 is the only supported syntaxthe pushback and concerns a specially from library authors could be addressed by giving a clear deprecation period.
I very much like this proposal, but also am a bit concerned about the negative impact.
So big 👍 for @odersky and everyone else who participates in such important discussions
The amount of issues and conflicts that have arisen with :=, especially since the original example was
def incr = x = x + 1
makes @lihaoyi's suggested solution of forcing curly braces much more appealing than a walrus operator.
I know it's been said before, but not only are you _saving_ a reserved operator, you don't have to teach anything new to anyone who already knows what {} does. Additionally, you can have the compiler treat a var assignment as a statement as @lihaoyi said, and have it automatically convert to an expression & spit out a warning during the period between 3.x and 3.y.
I very rarely use vars in my code but I commonly use DSLs with :=, so that's the biggest part of this issue that would affect me (writing `:=` everywhere).
I very rarely use
vars in my code but I commonly use DSLs with:=, so that's the biggest part of this issue that would affect me (writing:=everywhere).
As I understand it, this proposal does not prevent using := in DSL (without backticks).
Speaking of IDEs:
: inlay hint in assignments to varsvar identifiers look differentUnit type is used as a value@pavelfatin I like it!
I've used it for years in a language called, Simple Build Tool, I didn't like it when I first came across it and I don't like it now. Such a change would only make sense if the ==, was being changed to = for equality.
Most helpful comment
I'm sorry, Martin, but I think you're making a serious mistake here in not seeing this from the viewpoint of the companies (like the one I work in) that have dozens-to-hundreds of Scala engineers who need retraining. Every little bit of change here adds up in a serious and dangerous way, and the syntactic shifts make it all vastly scarier to those communities. Automatic rewriting of the code doesn't help -- it's peoples' brains and habits that are the issue here, and the price of these changes can easily run to hundreds of thousands of dollars in the short-to-medium term. Folks aren't going to casually take your word for it that we're going to recoup that in increased productivity eventually.
When the Scala 3 project started out, the message, loud and clear, was "We aren't going to split the community". I think that, by suggesting so much change at once, you are severely increasing the odds of doing exactly that. Plain and simply, you have scared the heck out of many of us who are Scala evangelists at our companies. I don't disagree with most of the individual changes (although I'm still deeply skeptical about the braceless style), and might well agree with most of them if they were spread out over time. But IMO it's terribly important not to slam it all in at once, or we're likely to lose large numbers of people -- and worse, large numbers of companies who decide that this is too much headache.
We need to have more patience, to recognize that the language is going to keep evolving, and focus on creating a solid foundation in 3.0 rather than a huge big-bang change...