Skip to content

Add code tabs to Syntactic Changes #2722

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

Merged
merged 7 commits into from
Mar 10, 2023
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 119 additions & 68 deletions _overviews/scala3-migration/incompat-syntactic.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,135 +38,172 @@ It is composed of:
- `=>>`
- `?=>`

For instance, the following piece of code can be compiled with Scala 2.13 but not wtih Scala 3.
{% tabs scala-3-keywords_1 %}
{% tab 'Scala 3 Only' %}

```scala
object given { // Error: given is now a keyword
val enum = ??? // Error: enum is now a keyword
For instance, the following piece of code can be compiled with Scala 2.13 but not with Scala 3.
~~~ scala
object given { //Error: given is now a keyword
val enum = ??? //Error: enum is now a keyword

println(enum) // Error: enum is now a keyword
println(enum) //Error: enum is now a keyword
}
```
~~~
{% endtab %}
{% endtabs %}

The [Scala 3 migration compilation](tooling-migration-mode.html) rewrites the code into:
{% tabs scala-3-keywords_2 %}
{% tab 'Scala 3 Only' %}

{% highlight diff %}
-object given {
+object `given` {
- val enum = ???
+ val `enum` = ???
The [Scala 3 migration compilation](tooling-migration-mode.html) rewrites the code into:
~~~ scala
object `given` {
val `enum` = ???

- println(enum)
+ println(`enum`)
println(`enum`)
}
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}

## Procedure Syntax

Procedure syntax has been deprecated for a while and it is dropped in Scala 3.
The following pieces of code are now illegal:

```scala
{% tabs scala-3-procedure_1 %}
{% tab 'Scala 3 Only' %}

The following pieces of code are now illegal:
~~~ scala
object Bar {
def print() { // Error: Procedure syntax no longer supported; `: Unit =` should be inserted here
def print() { //Error: Procedure syntax no longer supported; `: Unit =` should be inserted here
println("bar")
}
}
```
~~~
{% endtab %}
{% endtabs %}

The [Scala 3 migration compilation](tooling-migration-mode.html) rewrites the code into.
{% tabs scala-3-procedure_2 %}
{% tab 'Scala 3 Only' %}

{% highlight diff %}
The [Scala 3 migration compilation](tooling-migration-mode.html) rewrites the code into.
~~~ scala
object Bar {
- def print() {
+ def print(): Unit = {
def print(): Unit = {
println("bar")
}
}
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}

## Parentheses Around Lambda Parameter

When followed by its type, the parameter of a lambda is now required to be enclosed in parentheses.
The following piece of code is invalid.

```scala
val f = { x: Int => x * x } // Error: parentheses are required around the parameter of a lambda
```
{% tabs scala-3-lambda_1 %}
{% tab 'Scala 3 Only' %}
~~~ scala
val f = { x: Int => x * x } //Error: parentheses are required around the parameter of a lambda
~~~
{% endtab %}
{% endtabs %}

The [Scala 3 migration compilation](tooling-migration-mode.html) rewrites the code into:
{% tabs scala-3-lambda_2 %}
{% tab 'Scala 3 Only' %}

{% highlight diff %}
-val f = { x: Int => x * x }
+val f = { (x: Int) => x * x }
{% endhighlight %}
The [Scala 3 migration compilation](tooling-migration-mode.html) rewrites the code into:
~~~ scala
val f = { (x: Int) => x * x }
~~~
{% endtab %}
{% endtabs %}

## Open Brace Indentation For Passing An Argument

In Scala 2 it is possible to pass an argument after a new line by enclosing it into braces.
Although valid, this style of coding is not encouraged by the [Scala style guide](https://docs.scala-lang.org/style) and is no longer supported in Scala 3.

This syntax is now invalid:
```scala
{% tabs brace_1 class=tabs-scala-version %}
{% tab 'Scala 2' for=brace_1 %}
~~~ scala
test("my test")
{ // Error: This opening brace will start a new statement in Scala 3.
{
assert(1 == 1)
}
```
~~~
{% endtab %}
{% tab 'Scala 3' for=brace_1 %}

The [Scala 3 migration compiler](tooling-migration-mode.html) indents the first line of the block.

{% highlight diff %}
~~~ scala
test("my test")
-{
+ {
{
assert(1 == 1)
}
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}

This migration rule applies to other patterns as well, such as refining a type after a new line.

{% highlight diff %}
{% tabs scala-3-brace_2 %}
{% tab 'Scala 3 Only' %}
~~~ scala
type Bar = Foo
- {
+ {
{
def bar(): Int
}
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}

A preferable solution is to write:

{% highlight diff %}
-test("my test")
-{
+test("my test") {
{% tabs scala-3-brace_3 %}
{% tab 'Scala 3 Only' %}
~~~ scala
test("my test") {
assert(1 == 1)
}
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}


## Wrong indentation

The Scala 3 compiler now requires correct indentation.
The following piece of code, that was compiled in Scala 2.13, does not compile anymore because of the indentation.

```scala
{% tabs scala-3-indentation_1 %}
{% tab 'Scala 3 Only' %}

~~~ scala
def bar: (Int, Int) = {
val foo = 1.0
val bar = foo // [E050] Type Error: value foo does not take parameters
(1, 1)
} // [E007] Type Mismatch Error: Found Unit, Required (Int, Int)
```
~~~
{% endtab %}
{% endtabs %}

The indentation must be fixed.
{% tabs scala-3-indentation_2 %}
{% tab 'Scala 3 Only' %}

{% highlight diff %}
The indentation must be fixed.
~~~ scala
def bar: (Int, Int) = {
val foo = 1.0
val bar = foo
- (1, 1)
+ (1, 1)
(1, 1)
}
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}

These errors can be prevented by using a Scala formatting tool such as [scalafmt](https://scalameta.org/scalafmt/) or the [IntelliJ Scala formatter](https://www.jetbrains.com/help/idea/reformat-and-rearrange-code.html).
Beware that these tools may change the entire code style of your project.
Expand All @@ -176,44 +213,58 @@ Beware that these tools may change the entire code style of your project.
The usage of the `_` identifier as a type parameter is permitted in Scala 2.13, even if it has never been mentioned in the Scala 2 specification.
It is used in the API of [fastparse](https://index.scala-lang.org/lihaoyi/fastparse), in combination with a context bound, to declare an implicit parameter.


```scala
{% tabs scala-2-identifier_1 %}
{% tab 'Scala 2 Only' %}
~~~ scala
def foo[_: Foo]: Unit = ???
```
~~~
{% endtab %}
{% endtabs %}

Here, the method `foo` takes a type parameter `_` and an implicit parameter of type `Foo[_]` where `_` refers to the type parameter, not the wildcard symbol.

Martin Odersky described this pattern as a "clever exploit of a scalac compiler bug" ([source](https://www.reddit.com/r/scala/comments/fczcvo/mysterious_context_bounds_in_fastparse_2/fjecokn/)).

The Scala 3 compiler does not permit this pattern anymore:

{% highlight text %}
{% tabs scala-3-identifier_2 %}
{% tab 'Scala 3 Only' %}
~~~ scala
-- [E040] Syntax Error: src/main/scala/anonymous-type-param.scala:4:10
4 | def foo[_: Foo]: Unit = ()
| ^
| an identifier expected, but '_' found
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}

The solution is to give the parameter a valid identifier name, for instance `T`.
This will not break the binary compatibility.

{% highlight diff %}
-def foo[_: Foo]: Unit = ???
+def foo[T: Foo]: Unit = ???
{% endhighlight %}
{% tabs scala-3-identifier_3 %}
{% tab 'Scala 3 Only' %}
~~~ scala
def foo[T: Foo]: Unit = ???
~~~
{% endtab %}
{% endtabs %}

## `+` And `-` As Type Parameters

`+` and `-` are not valid identifiers for type parameters in Scala 3, since they are reserved for variance annotation.

You cannot write `def foo[+]` or `def foo[-]` anymore.

{% highlight text %}
{% tabs scala-3-identifier_4 %}
{% tab 'Scala 3 Only' %}
~~~ scala
-- Error: src/main/scala/type-param-identifier.scala:2:10
2 | def foo[+]: +
| ^
| no `+/-` variance annotation allowed here
{% endhighlight %}
~~~
{% endtab %}
{% endtabs %}

The solution is to choose another valid identifier, for instance `T`.

Expand Down