Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use := for Assignment #7598

Closed
odersky opened this issue Nov 21, 2019 · 114 comments

Comments

@odersky
Copy link
Contributor

@odersky odersky commented Nov 21, 2019

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.

@lrytz

This comment has been minimized.

Copy link
Contributor

@lrytz lrytz commented Nov 21, 2019

Could you clarify if array(i) = x would stay the same, or be written array(i) := x?

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

I believe it should be written array(i) := x.

@sjrd

This comment has been minimized.

Copy link
Member

@sjrd sjrd commented Nov 21, 2019

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)
@jducoeur

This comment has been minimized.

Copy link
Contributor

@jducoeur jducoeur commented Nov 21, 2019

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

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

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.

@dwijnand

This comment has been minimized.

Copy link
Contributor

@dwijnand dwijnand commented Nov 21, 2019

What would you suggest to user-land code that made use of :=, like sbt? Outside of being able to backtick-escape :=.

@smarter

This comment has been minimized.

Copy link
Member

@smarter smarter commented Nov 21, 2019

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).

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

@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).

@sjrd

This comment has been minimized.

Copy link
Member

@sjrd sjrd commented Nov 21, 2019

Hum but then how do you actually assign something to a var whose type has a := method?

@sjrd

This comment has been minimized.

Copy link
Member

@sjrd sjrd commented Nov 21, 2019

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)
@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

Yes, setters are an issue. For compatibility we probably have to leave them as x_=, even though it's not ideal.

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

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.

@dwijnand

This comment has been minimized.

Copy link
Contributor

@dwijnand dwijnand commented Nov 21, 2019

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"
@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

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.

@AleksanderBG

This comment has been minimized.

Copy link
Contributor

@AleksanderBG AleksanderBG commented Nov 21, 2019

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.

@soronpo

This comment has been minimized.

Copy link
Contributor

@soronpo soronpo commented Nov 21, 2019

What about extension methods (currently via implicit classes) that use :=?

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

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.

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 21, 2019

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.

@soronpo

This comment has been minimized.

Copy link
Contributor

@soronpo soronpo commented Nov 21, 2019

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.

@liufengyun

This comment has been minimized.

Copy link
Contributor

@liufengyun liufengyun commented Nov 21, 2019

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:

  • 1 plus 1 is 2
  • The movie is great
  • "He is free now", the judge says (effects here!)
  • Tom is a student
  • There is a cat

In each sentence, the meaning of is is different.

@lihaoyi

This comment has been minimized.

Copy link
Contributor

@lihaoyi lihaoyi commented Nov 22, 2019

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

@nafg

This comment has been minimized.

Copy link

@nafg nafg commented Nov 22, 2019

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 22, 2019

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.

@jdegoes

This comment has been minimized.

Copy link

@jdegoes jdegoes commented Nov 22, 2019

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)
@wojtek-tg

This comment has been minimized.

Copy link

@wojtek-tg wojtek-tg commented Nov 22, 2019

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.

@soronpo

This comment has been minimized.

Copy link
Contributor

@soronpo soronpo commented Nov 22, 2019

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 was once implemented in the Virtualized Scala fork.

@lbialy

This comment has been minimized.

Copy link

@lbialy lbialy commented Nov 22, 2019

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.

@LPTK

This comment has been minimized.

Copy link
Contributor

@LPTK LPTK commented Nov 22, 2019

@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 { ... }
@ilhansubasi

This comment has been minimized.

Copy link

@ilhansubasi ilhansubasi commented Nov 22, 2019

Another option is to use <-. In R you can assign variables like this:
var1 <- 11
or
22 -> var2

@lihaoyi-databricks

This comment has been minimized.

Copy link

@lihaoyi-databricks lihaoyi-databricks commented Nov 22, 2019

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.

@nafg

This comment has been minimized.

Copy link

@nafg nafg commented Nov 24, 2019

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.

@lihaoyi

This comment has been minimized.

Copy link
Contributor

@lihaoyi lihaoyi commented Nov 25, 2019

@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

@eatkins

This comment has been minimized.

Copy link

@eatkins eatkins commented Nov 25, 2019

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.

@eatkins

This comment has been minimized.

Copy link

@eatkins eatkins commented Nov 25, 2019

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.

@lihaoyi

This comment has been minimized.

Copy link
Contributor

@lihaoyi lihaoyi commented Nov 25, 2019

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.

@eatkins

This comment has been minimized.

Copy link

@eatkins eatkins commented Nov 25, 2019

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.

@nafg

This comment has been minimized.

Copy link

@nafg nafg commented Nov 25, 2019

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 25, 2019

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.

@sirthias

This comment has been minimized.

Copy link

@sirthias sirthias commented Nov 25, 2019

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.

@15532

This comment has been minimized.

Copy link

@15532 15532 commented Nov 25, 2019

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.

@nafg

This comment has been minimized.

Copy link

@nafg nafg commented Nov 25, 2019

@Jasper-M

This comment has been minimized.

Copy link
Contributor

@Jasper-M Jasper-M commented Nov 25, 2019

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.

@dwijnand

This comment has been minimized.

Copy link
Contributor

@dwijnand dwijnand commented Nov 25, 2019

If you need a graceful migration, then you need a period in which both are supported. Otherwise, you can't change things.

@lihaoyi

This comment has been minimized.

Copy link
Contributor

@lihaoyi lihaoyi commented Nov 25, 2019

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.

@Jasper-M

This comment has been minimized.

Copy link
Contributor

@Jasper-M Jasper-M commented Nov 25, 2019

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.

@bishabosha

This comment has been minimized.

Copy link
Member

@bishabosha bishabosha commented Nov 25, 2019

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

@Sciss

This comment has been minimized.

Copy link

@Sciss Sciss commented Nov 25, 2019

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.

@dwijnand

This comment has been minimized.

Copy link
Contributor

@dwijnand dwijnand commented Nov 25, 2019

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.

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 25, 2019

@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.

@lihaoyi

This comment has been minimized.

Copy link
Contributor

@lihaoyi lihaoyi commented Nov 25, 2019

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 25, 2019

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

  • We have to leave setters x_= as they are; so that would give an inconsistency wrt :=.
  • The cost of migration would be high.

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.

@texasbruce

This comment has been minimized.

Copy link

@texasbruce texasbruce commented Nov 25, 2019

@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.

@nafg

This comment has been minimized.

Copy link

@nafg nafg commented Nov 25, 2019

@fkowal

This comment has been minimized.

Copy link

@fkowal fkowal commented Nov 25, 2019

@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,

  1. Scala 3 it's totally optional, where x = x + 1 and x := x + 1 are valid and mean exactly the same thing
  2. Scala 3.x x = x + 1 becomes a deprecated
  3. Scala 3.y x := x + 1 is the only supported syntax

the 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

@sorenbug

This comment has been minimized.

Copy link

@sorenbug sorenbug commented Nov 25, 2019

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).

@soronpo

This comment has been minimized.

Copy link
Contributor

@soronpo soronpo commented Nov 25, 2019

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).

@pavelfatin

This comment has been minimized.

Copy link

@pavelfatin pavelfatin commented Nov 27, 2019

Speaking of IDEs:

@odersky

This comment has been minimized.

Copy link
Contributor Author

@odersky odersky commented Nov 27, 2019

@pavelfatin I like it!

@odersky odersky closed this Nov 28, 2019
@Rich2

This comment has been minimized.

Copy link

@Rich2 Rich2 commented Nov 30, 2019

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.