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

Disallow _ as an identifier #10384

Closed
szeiger opened this issue Jun 21, 2017 · 12 comments
Closed

Disallow _ as an identifier #10384

szeiger opened this issue Jun 21, 2017 · 12 comments

Comments

@szeiger
Copy link

szeiger commented Jun 21, 2017

Currently the lexical syntax allows a single underscore _ as a valid identifier because it is treated as an upper-case character. This leads to inconsistencies in the syntax because in many (but not in all) contexts the underscore has special meaning and is therefore not a valid identifier:

val _ = 1 // Identifier, even though it could also be a simple pattern
var _ = 1 // The same
def _ = 1 // Syntax error
println(_) // Eta expansion

Implicit vals are a dangerous corner case where the difference between an identifier and a simple pattern becomes important:

implicit val (_, _) = (42, "foo") // _ treated as simple pattern
implicitly[Int] // Does not compile

implicit val _ = 42 // _ treated as identifier
implicitly[Int] // returns 42

I propose to change the lexical syntax so that a single underscore is no longer a valid identifier (unless it is enclosed in backticks). When used in a val or var definition it should be treated as a simple pattern (in the way in which is is already specified).

@szeiger szeiger changed the title Disallow _ as an identifier Disallow _ as an identifier Jun 21, 2017
@propensive
Copy link

As another datapoint, _ is also valid as a "don't care" identifier in a lambda, e.g. List(1, 2, 3).map { _ => println("foo") }.

@som-snytt
Copy link

som-snytt commented Jun 23, 2017

Underscore is lower case. scala/scala#5919

It's underscore, not upperscore or overscore.

Also #6426

I was going to say:

$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_65).
Type in expressions for evaluation. Or try :help.

scala> object X { val _ = 42 }
defined object X

scala> { import X._ ; 42 match { case x @ `_` => x } }
exception when typing <_: error>.==(x1)/class scala.reflect.internal.Trees$Apply
not found: value _ in file <console>
scala.reflect.internal.Types$TypeError: not found: value _

but I guess I won't. (The import is not relevant to the crash.)

Wasn't there a movement underfoot or perhaps a motion for underscore to introduce a fresh name? with caveats around stability of member names. I may be conflating underscore with the consultancy.

implicit val _: Int   = 42 // implicitly[Int] is $x1
implicit val (_): Int = 42 // introduces nothing
implicit val _: _     = 42 // escape hatch for unproblematic typing?

They're going to make us supply types of implicits, must we supply names, too?

@szeiger
Copy link
Author

szeiger commented Jun 23, 2017

Wasn't there a movement underfoot or perhaps a motion for underscore to introduce a fresh name?

Using underscores to not introduce a name is essential for un-imports and very convenient for pattern matching. You wouldn't want to bind lots of vals you don't care about. For consistency it would be nice to get the same behavior in other cases as well, rather than introducing fresh names (or binding to _).

As another datapoint, _ is also valid as a "don't care" identifier in a lambda, e.g. List(1, 2, 3).map { _ => println("foo") }

In Scala 2.12 this doesn't bind to _:

scala> val f: (Int => Int) = { _ => `_` }
<console>:11: error: not found: value _
       val f: (Int => Int) = { _ => `_` }

For some reason Dotty doesn't like a quoted _:

scala> val f: (Int => Int) = { _ => `_` }
-- Error: <console>:1:29 -------------------------------------------------------
1 |val f: (Int => Int) = { _ => `_` }
  |                             ^
  |                             wildcard invalid as backquoted identifier

But by writing an implicit function we can observe that the implicit parameter is indeed bound to a name:

scala> val f: (Int => Int) = { implicit _ => implicitly[Int] }
val f: Int => Int = $$Lambda$10/479160976@3050ac2f

@szeiger
Copy link
Author

szeiger commented Jun 23, 2017

BTW, accidental assignment to _ also breaks the REPL:

No problem here:

scala> { val _ = 42 }

Here the REPL tries to create code to print the result (and fails):

scala> val _ = 42
<console>:5: error: identifier expected but '_' found.
  lazy val $result = _
                                          ^
<console>:10: error: identifier expected but '_' found.
 + "_: Int = " + _root_.scala.runtime.ScalaRunTime.replStringOf(_, 1000)
                                                                                     ^

@dwijnand
Copy link
Member

dwijnand commented Dec 6, 2017

In case it is decided not to disallow _ as an identifier I extracted the REPL bug into it's own issue: #10645.

@Atry
Copy link

Atry commented Dec 7, 2017

But by writing an implicit function we can observe that the implicit parameter is indeed bound to a name:

scala> val f: (Int => Int) = { implicit _ => implicitly[Int] }
val f: Int => Int = $$Lambda$10/479160976@3050ac2f

@szeiger Is it possible to extend this behavior to support implicit def as well?

object Implicits {
  // Generate a term name from the method type
  implicit def _: Ordering[BigInteger] = ???
}

import Implicits._
implicitly[Ordering[BigInteger]]

@som-snytt
Copy link

The canonical ticket is #7691

The anon fun syntax is covered by 6.23, which says you get a fresh name for underscore; but it doesn't include implicit underscore; maybe that was added by popular demand?

The other "fresh name" semantics is what I asked about in a previous comment. Maybe that will also succumb to popular demand. After the # metoo movement, I believe that the people have the power.

@Atry
Copy link

Atry commented Dec 7, 2017

For public methods, "fresh name" may be not a good idea to keep backward compatibility. Hash naming based on the method type may be a better choice.

@som-snytt
Copy link

I submitted a fix at #7691

I think most of the info is on the other ticket?

@xuwei-k
Copy link

xuwei-k commented Jan 22, 2019

FYI com-lihaoyi/fastparse#214

@SethTisue
Copy link
Member

@som-snytt should this ticket still be open?

@som-snytt
Copy link

@szeiger might have another idea, but I think the edge cases were covered in some form:

scala> implicit val _ = 42 ; implicitly[Int]
                                       ^
       error: could not find implicit value for parameter e: Int

scala> def _ = 1
           ^
       error: identifier expected but '_' found.

scala> val f: (Int => Int) = { implicit _ => implicitly[Int] }
                                        ^
       error: expected start of definition

I think the speculative part about using underscore to introduce anonymous implicits is still an open issue, but deserves its own ticket if there isn't one. Also, to extend the metaphor, for for:

scala> for (implicit _ <- List(42)) yield implicitly[Int]
            ^
       error: illegal start of simple pattern

Also, arguably implicit should error if nothing is bound. If it doesn't throw, then normally the implicit is available:

implicit val Extractor(x) = expr   // either succeeds and implicit is available or throws MatchError

rtyley added a commit to guardian/twitter4s that referenced this issue Oct 20, 2019
This project now compiles with Scala 2.13 by default, while still
cross-compiling to Scala 2.12.

Issues addressed with this upgrade:

### Underscore (`_`) is no longer a valid identifer for vals

Using underscore like this is no longer allowed...

```
implicit val _ = ...
```

...we have to give the `val` a valid name:


```
implicit val v = ...
```

See also:

* scala/bug#10384
* scala/bug#7691
* scala/scala#6218

### `Map.mapValues()` now returns a `MapView` rather than a `Map`

We usually want a `Map`, so we can just add in a `.toMap` to the end of
the expression - it's harmless in Scala 2.12, and allows the code to
compile in Scala 2.13. `Map.mapValues()` itself is deprecated in Scala
2.13, but using the new recommended alternative doesn't work in Scala 2.12.

https://docs.scala-lang.org/overviews/core/collections-migration-213.html#what-are-the-breaking-changes

See also:

* scala/bug#10919
* scala/scala#7014
rtyley added a commit to guardian/twitter4s that referenced this issue Oct 20, 2019
This project now compiles with Scala 2.13 by default, while still
cross-compiling to Scala 2.12.

Issues addressed with this upgrade:

### Underscore (`_`) is no longer a valid identifer for `val`s

Using underscore like this is no longer allowed...

```
implicit val _ = ...
```

...we have to give the `val` a valid name (even if it's just `v`):


```
implicit val v = ...
```

See also:

* scala/bug#10384
* scala/bug#7691
* scala/scala#6218

### `Map.mapValues()` now returns a `MapView` rather than a `Map`

We usually want a `Map`, so we can just add in a `.toMap` to the end of
the expression - it's harmless in Scala 2.12, and allows the code to
compile in Scala 2.13. `Map.mapValues()` itself is deprecated in Scala
2.13, but using the new recommended alternative doesn't work in Scala 2.12.

https://docs.scala-lang.org/overviews/core/collections-migration-213.html#what-are-the-breaking-changes

See also:

* scala/bug#10919
* scala/scala#7014

### `.to` can no longer infer what resulting collection type you want

This is all part of `CanBuildFrom` going away in Scala 2.13:

https://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#life-without-canbuildfrom
rtyley added a commit to guardian/twitter4s that referenced this issue Oct 20, 2019
This project now compiles with Scala 2.13 by default, while still
cross-compiling to Scala 2.12 & 2.11.

Issues addressed with this upgrade:

### Underscore (`_`) is no longer a valid identifer for `val`s

Using underscore like this is no longer allowed...

```
implicit val _ = ...
```

...we have to give the `val` a valid name (even if it's just `v`):


```
implicit val v = ...
```

See also:

* scala/bug#10384
* scala/bug#7691
* scala/scala#6218

### `Map.mapValues()` now returns a `MapView` rather than a `Map`

We usually want a `Map`, so we can just add in a `.toMap` to the end of
the expression - it's harmless in Scala 2.12, and allows the code to
compile in Scala 2.13. `Map.mapValues()` itself is deprecated in Scala
2.13, but using the new recommended alternative doesn't work in Scala 2.12.

https://docs.scala-lang.org/overviews/core/collections-migration-213.html#what-are-the-breaking-changes

See also:

* scala/bug#10919
* scala/scala#7014

### `.to` can no longer infer what resulting collection type you want

This is all part of `CanBuildFrom` going away in Scala 2.13:

https://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#life-without-canbuildfrom
rtyley added a commit to guardian/twitter4s that referenced this issue Oct 20, 2019
This project now compiles with Scala 2.13 by default, while still
cross-compiling to Scala 2.12 & 2.11.

Issues addressed with this upgrade:

### Underscore (`_`) is no longer a valid identifer for `val`s

Using underscore like this is no longer allowed...

```
implicit val _ = ...
```

...we have to give the `val` a valid name (even if it's just `v`):


```
implicit val v = ...
```

See also:

* scala/bug#10384
* scala/bug#7691
* scala/scala#6218

### `Map.mapValues()` now returns a `MapView` rather than a `Map`

We usually want a `Map`, so we can just add in a `.toMap` to the end of
the expression - it's harmless in Scala 2.12, and allows the code to
compile in Scala 2.13. `Map.mapValues()` itself is deprecated in Scala
2.13, but using the new recommended alternative doesn't work in Scala 2.12.

https://docs.scala-lang.org/overviews/core/collections-migration-213.html#what-are-the-breaking-changes

See also:

* scala/bug#10919
* scala/scala#7014

### `.to` can no longer infer what resulting collection type you want

This is all part of `CanBuildFrom` going away in Scala 2.13:

https://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#life-without-canbuildfrom
rtyley added a commit to guardian/twitter4s that referenced this issue Oct 21, 2019
This project now compiles with Scala 2.13 by default, while still
cross-compiling to Scala 2.12 & 2.11.

Issues addressed with this upgrade:

### Underscore (`_`) is no longer a valid identifer for `val`s

Using underscore like this is no longer allowed...

```
implicit val _ = ...
```

...we have to give the `val` a valid name (even if it's just `v`):


```
implicit val v = ...
```

See also:

* scala/bug#10384
* scala/bug#7691
* scala/scala#6218

### `Map.mapValues()` now returns a `MapView` rather than a `Map`

We usually want a `Map`, so we can just add in a `.toMap` to the end of
the expression - it's harmless in Scala 2.12, and allows the code to
compile in Scala 2.13. `Map.mapValues()` itself is deprecated in Scala
2.13, but using the new recommended alternative doesn't work in Scala 2.12.

https://docs.scala-lang.org/overviews/core/collections-migration-213.html#what-are-the-breaking-changes

See also:

* scala/bug#10919
* scala/scala#7014

### `.to` can no longer infer what resulting collection type you want

This is all part of `CanBuildFrom` going away in Scala 2.13:

https://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#life-without-canbuildfrom
rtyley added a commit to guardian/twitter4s that referenced this issue Oct 21, 2019
This project now compiles with Scala 2.13 by default, while still
cross-compiling to Scala 2.12 & 2.11.

Although Scala 2.13.1 has been released, this change sticks with 2.13.0,
as `scoverage` has not yet released an update compatible with 2.13.1:

scoverage/sbt-scoverage#295
scoverage/scalac-scoverage-plugin#283
scoverage/sbt-scoverage#299


Migration issues addressed with the update to Scala 2.13:

### Underscore (`_`) is no longer a valid identifer for `val`s

Using underscore like this is no longer allowed...

```
implicit val _ = ...
```

...we have to give the `val` a valid name (even if it's just `v`):


```
implicit val v = ...
```

See also:

* scala/bug#10384
* scala/bug#7691
* scala/scala#6218

### `Map.mapValues()` now returns a `MapView` rather than a `Map`

We usually want a `Map`, so we can just add in a `.toMap` to the end of
the expression - it's harmless in Scala 2.12, and allows the code to
compile in Scala 2.13. `Map.mapValues()` itself is deprecated in Scala
2.13, but using the new recommended alternative doesn't work in Scala 2.12.

https://docs.scala-lang.org/overviews/core/collections-migration-213.html#what-are-the-breaking-changes

See also:

* scala/bug#10919
* scala/scala#7014

### `.to` can no longer infer what resulting collection type you want

This is all part of `CanBuildFrom` going away in Scala 2.13:

https://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#life-without-canbuildfrom
rtyley added a commit to guardian/twitter4s that referenced this issue Oct 21, 2019
This project now compiles with Scala 2.13 by default, while still
cross-compiling to Scala 2.12 & 2.11.

Although Scala 2.13.1 has been released, this change sticks with 2.13.0,
as `scoverage` has not yet released an update compatible with 2.13.1:

scoverage/sbt-scoverage#295
scoverage/scalac-scoverage-plugin#283
scoverage/sbt-scoverage#299


Migration issues addressed with the update to Scala 2.13:

### Underscore (`_`) is no longer a valid identifer for `val`s

Using underscore like this is no longer allowed...

```
implicit val _ = ...
```

...we have to give the `val` a valid name (even if it's just `v`):


```
implicit val v = ...
```

See also:

* scala/bug#10384
* scala/bug#7691
* scala/scala#6218

### `Map.mapValues()` now returns a `MapView` rather than a `Map`

We usually want a `Map`, so we can just add in a `.toMap` to the end of
the expression - it's harmless in Scala 2.12, and allows the code to
compile in Scala 2.13. `Map.mapValues()` itself is deprecated in Scala
2.13, but using the new recommended alternative doesn't work in Scala 2.12.

https://docs.scala-lang.org/overviews/core/collections-migration-213.html#what-are-the-breaking-changes

See also:

* scala/bug#10919
* scala/scala#7014

### `.to` can no longer infer what resulting collection type you want

This is all part of `CanBuildFrom` going away in Scala 2.13:

https://www.scala-lang.org/blog/2018/06/13/scala-213-collections.html#life-without-canbuildfrom
@SethTisue SethTisue added this to the 2.13.0-RC1 milestone Feb 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants