question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Identifier shadowing in for-comprehensions is only supported in `flatMap`s

See original GitHub issue

In dotc 0.7.0, this compiles,

for {
  x <- List(1, 2, 3)
  x <- List(x + 1)
} yield x

but this does not,

for {
  x <- List(1, 2, 3)
  x = x + 1
} yield x

It gives the errors in the REPL,

<console>:14: error: recursive value x needs type
         x = x + 1
               ^
<console>:14: error: x is already defined as value x
         x = x + 1
         ^

It’s probably an accident of implementation, but it’s very useful for getting safe mutable-like syntax in long chained for-comprehensions, without needing to resort to introducing many different identifier names, provided every generator in the for-comprehension is a flatMap, not a map.

This is a quirk of Scala 2, and it would be nice to remove it in Scala 3.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:3
  • Comments:19 (12 by maintainers)

github_iconTop GitHub Comments

36reactions
LPTKcommented, May 17, 2018

I just wanted to add that Scala lacking shadowing of local variable is a huge usability problem IMHO. In functional languages like OCaml, where values are usually immutable, you can do:

let name = getName() in
let name = name |> capitalize in
print_string name

In Scala, you have to come up with silly names that pollute the namespace:

val name = getName()
val nameCap = name.capitalize
println(nameCap)

…and in my experience, this is actually a huge source of mistakes. It is all too easy to write println(name), forgetting about the more recent version of the value, nameCap. This is especially terrible if you introduce the intermediate transformation in the middle of existing code, and you have to find and change all usages of the old version in the rest of the function. Shadowing actually helps users by preventing later code from seeing an obsolete binding.

Now, it’s probably too late to change the scoping rules of Scala, but maybe something can be done to alleviate this usability hazard in a backward-compatible way. How about a syntax like this?

val name = getName()
new val name = name.capitalize
println(name)

Then we can then say that for-comprehension bindings have the semantics of new val shadowing bindings instead of the semantics of simple val bindings (and we can make the desugaring itself actually use new val).

4reactions
LPTKcommented, May 18, 2018

@Sciss the argument is that inventing new names is hard, so at some point you’re going to use dummy names like name0, name1 etc. which is a big potential for mistakes (it’s easy to leave some reference to name2 instead of name3 somewhere). Moreover, in the motivating example the new variable still conceptually represents the name, even though the type is refined, so there is arguably no reason for picking a different name and polluting the local namespace.

Anyway, at this point I guess it depends on individual preferences. Though I think it’s valuable to remember that ML programmers have been doing this for a long time without problems (AFAIK).

Read more comments on GitHub >

github_iconTop Results From Across the Web

تويتر \ Lionel Parreaux (LParreaux@) - Twitter
Many programming languages in the OO tradition now support pattern matching ... Identifier shadowing in for-comprehensions is only supported in `flatMap`s ...
Read more >
A Comprehensive Guide to For-Comprehension in Scala
Take a deep dive into Scala's for-comprehension construct.
Read more >
Scala Standard Library 2.13.5 - scala.Option - Javadoc.io
final def flatMap[B](f: (A) => Option[B]): Option[B] ... Implicit conversion to Iterable[(A, B)] is also supported. ... Iterable in for comprehensions.
Read more >
Scala Standard Library 2.13.3 - scala.Option
... to treat it as a collection or monad and use map , flatMap , filter , or foreach : ... flatMap —...
Read more >
Introduction to Programming Languages - Jaemin Hong
classes are similar to classes but more convenient, e.g. automatic support for pretty printing and pattern matching. The syntax of a class definition...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found