forked from scala/scala3
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
reference taken from scala@7919cb3
- Loading branch information
Showing
121 changed files
with
15,330 additions
and
0 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
docs/_spec/TODOreference/changed-features/changed-features.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
layout: index | ||
title: "Other Changed Features" | ||
nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features.html | ||
--- | ||
|
||
The following pages document the features that have changed in Scala 3, compared to Scala 2. |
128 changes: 128 additions & 0 deletions
128
docs/_spec/TODOreference/changed-features/compiler-plugins.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
--- | ||
layout: doc-page | ||
title: "Changes in Compiler Plugins" | ||
nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/compiler-plugins.html | ||
--- | ||
|
||
Compiler plugins are supported by Dotty (and Scala 3) since 0.9. There are two notable changes | ||
compared to `scalac`: | ||
|
||
- No support for analyzer plugins | ||
- Added support for research plugins | ||
|
||
[Analyzer plugins][1] in `scalac` run during type checking and may influence | ||
normal type checking. This is a very powerful feature but for production usages, | ||
a predictable and consistent type checker is more important. | ||
|
||
For experimentation and research, Scala 3 introduces _research plugin_. Research plugins | ||
are more powerful than `scalac` analyzer plugins as they let plugin authors customize | ||
the whole compiler pipeline. One can easily replace the standard typer by a custom one or | ||
create a parser for a domain-specific language. However, research plugins are only | ||
enabled for nightly or snaphot releases of Scala 3. | ||
|
||
Common plugins that add new phases to the compiler pipeline are called | ||
_standard plugins_ in Scala 3. In terms of features, they are similar to | ||
`scalac` plugins, despite minor changes in the API. | ||
|
||
## Using Compiler Plugins | ||
|
||
Both standard and research plugins can be used with `scalac` by adding the `-Xplugin:` option: | ||
|
||
```shell | ||
scalac -Xplugin:pluginA.jar -Xplugin:pluginB.jar Test.scala | ||
``` | ||
|
||
The compiler will examine the jar provided, and look for a property file named | ||
`plugin.properties` in the root directory of the jar. The property file specifies | ||
the fully qualified plugin class name. The format of a property file is as follows: | ||
|
||
```properties | ||
pluginClass=dividezero.DivideZero | ||
``` | ||
|
||
This is different from `scalac` plugins that required a `scalac-plugin.xml` file. | ||
|
||
Starting from 1.1.5, `sbt` also supports Scala 3 compiler plugins. Please refer to the | ||
[`sbt` documentation][2] for more information. | ||
|
||
## Writing a Standard Compiler Plugin | ||
|
||
Here is the source code for a simple compiler plugin that reports integer divisions by | ||
zero as errors. | ||
|
||
```scala | ||
package dividezero | ||
|
||
import dotty.tools.dotc.ast.Trees.* | ||
import dotty.tools.dotc.ast.tpd | ||
import dotty.tools.dotc.core.Constants.Constant | ||
import dotty.tools.dotc.core.Contexts.Context | ||
import dotty.tools.dotc.core.Decorators.* | ||
import dotty.tools.dotc.core.StdNames.* | ||
import dotty.tools.dotc.core.Symbols.* | ||
import dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin} | ||
import dotty.tools.dotc.transform.{Pickler, Staging} | ||
|
||
class DivideZero extends StandardPlugin: | ||
val name: String = "divideZero" | ||
override val description: String = "divide zero check" | ||
|
||
def init(options: List[String]): List[PluginPhase] = | ||
(new DivideZeroPhase) :: Nil | ||
|
||
class DivideZeroPhase extends PluginPhase: | ||
import tpd.* | ||
|
||
val phaseName = "divideZero" | ||
|
||
override val runsAfter = Set(Pickler.name) | ||
override val runsBefore = Set(Staging.name) | ||
|
||
override def transformApply(tree: Apply)(implicit ctx: Context): Tree = | ||
tree match | ||
case Apply(Select(rcvr, nme.DIV), List(Literal(Constant(0)))) | ||
if rcvr.tpe <:< defn.IntType => | ||
report.error("dividing by zero", tree.pos) | ||
case _ => | ||
() | ||
tree | ||
end DivideZeroPhase | ||
``` | ||
|
||
The plugin main class (`DivideZero`) must extend the trait `StandardPlugin` | ||
and implement the method `init` that takes the plugin's options as argument | ||
and returns a list of `PluginPhase`s to be inserted into the compilation pipeline. | ||
|
||
Our plugin adds one compiler phase to the pipeline. A compiler phase must extend | ||
the `PluginPhase` trait. In order to specify when the phase is executed, we also | ||
need to specify a `runsBefore` and `runsAfter` constraints that are list of phase | ||
names. | ||
|
||
We can now transform trees by overriding methods like `transformXXX`. | ||
|
||
## Writing a Research Compiler Plugin | ||
|
||
Here is a template for research plugins. | ||
|
||
```scala | ||
import dotty.tools.dotc.core.Contexts.Context | ||
import dotty.tools.dotc.core.Phases.Phase | ||
import dotty.tools.dotc.plugins.ResearchPlugin | ||
|
||
class DummyResearchPlugin extends ResearchPlugin: | ||
val name: String = "dummy" | ||
override val description: String = "dummy research plugin" | ||
|
||
def init(options: List[String], phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = | ||
phases | ||
end DummyResearchPlugin | ||
``` | ||
|
||
A research plugin must extend the trait `ResearchPlugin` and implement the | ||
method `init` that takes the plugin's options as argument as well as the compiler | ||
pipeline in the form of a list of compiler phases. The method can replace, remove | ||
or add any phases to the pipeline and return the updated pipeline. | ||
|
||
|
||
[1]: https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala | ||
[2]: https://www.scala-sbt.org/1.x/docs/Compiler-Plugins.html |
77 changes: 77 additions & 0 deletions
77
docs/_spec/TODOreference/changed-features/eta-expansion-spec.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
--- | ||
layout: doc-page | ||
title: "Automatic Eta Expansion - More Details" | ||
nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/eta-expansion-spec.html | ||
--- | ||
|
||
## Motivation | ||
|
||
Scala maintains a convenient distinction between _methods_ and _functions_. | ||
Methods are part of the definition of a class that can be invoked in objects while functions are complete objects themselves, making them first-class entities. For example, they can be assigned to variables. | ||
These two mechanisms are bridged in Scala by a mechanism called | ||
[_eta-expansion_](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#eta-expansion-section) | ||
(also called eta-abstraction), which converts a reference to a method into a function. Intuitively, a method `m` can be passed around by turning it into an object: the function `x => m(x)`. | ||
|
||
In this snippet which assigns a method to a `val`, the compiler will perform _automatic eta-expansion_, as shown in the comment: | ||
|
||
```scala | ||
def m(x: Int, y: String) = ??? | ||
val f = m // becomes: val f = (x: Int, y: String) => m(x, y) | ||
``` | ||
|
||
In Scala 2, a method reference `m` is converted to a function value only if the expected type is a function type, which means the conversion in the example above would not have been triggered, because `val f` does not have a type ascription. To still get eta-expansion, a shortcut `m _` would force the conversion. | ||
|
||
For methods with one or more parameters like in the example above, this restriction has now been dropped. The syntax `m _` is no longer needed and will be deprecated in the future. | ||
|
||
## Automatic eta-expansion and partial application | ||
In the following example `m` can be partially applied to the first two parameters. | ||
Assigning `m` to `f1` will automatically eta-expand. | ||
|
||
```scala | ||
def m(x: Boolean, y: String)(z: Int): List[Int] | ||
val f1 = m | ||
val f2 = m(true, "abc") | ||
``` | ||
|
||
This creates two function values: | ||
|
||
```scala | ||
f1: (Boolean, String) => Int => List[Int] | ||
f2: Int => List[Int] | ||
``` | ||
|
||
## Automatic eta-expansion and implicit parameter lists | ||
|
||
Methods with implicit parameter lists will always get applied to implicit arguments. | ||
|
||
```scala | ||
def foo(x: Int)(implicit p: Double): Float = ??? | ||
implicit val bla: Double = 1.0 | ||
|
||
val bar = foo // val bar: Int => Float = ... | ||
``` | ||
|
||
## Automatic Eta-Expansion and query types | ||
|
||
A method with context parameters can be expanded to a value of a context type by writing the expected context type explicitly. | ||
|
||
```scala | ||
def foo(x: Int)(using p: Double): Float = ??? | ||
val bar: Double ?=> Float = foo(3) | ||
``` | ||
|
||
## Rules | ||
|
||
- If `m` has an argument list with one or more parameters, we always eta-expand | ||
- If `m` is has an empty argument list (i.e. has type `()R`): | ||
1. If the expected type is of the form `() => T`, we eta expand. | ||
2. If m is defined by Java, or overrides a Java defined method, we insert `()`. | ||
3. Otherwise we issue an error of the form: | ||
|
||
Thus, an unapplied method with an empty argument list is only converted to a function when a function type is expected. It is considered best practice to either explicitly apply the method to `()`, or convert it to a function with `() => m()`. | ||
|
||
The method value syntax `m _` is deprecated. | ||
|
||
## Reference | ||
|
||
For more information, see [PR #2701](https://github.com/lampepfl/dotty/pull/2701). |
42 changes: 42 additions & 0 deletions
42
docs/_spec/TODOreference/changed-features/eta-expansion.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
--- | ||
layout: doc-page | ||
title: "Automatic Eta Expansion" | ||
nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/eta-expansion.html | ||
--- | ||
|
||
The conversion of _methods_ into _functions_ has been improved and happens automatically for methods with one or more parameters. | ||
|
||
```scala | ||
def m(x: Boolean, y: String)(z: Int): List[Int] | ||
val f1 = m | ||
val f2 = m(true, "abc") | ||
``` | ||
|
||
This creates two function values: | ||
```scala | ||
f1: (Boolean, String) => Int => List[Int] | ||
f2: Int => List[Int] | ||
``` | ||
|
||
The syntax `m _` is no longer needed and will be deprecated in the future. | ||
|
||
## Automatic eta-expansion and nullary methods | ||
|
||
Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list. | ||
|
||
```scala | ||
def next(): T | ||
``` | ||
|
||
Given a simple reference to `next` does not auto-convert to a function. | ||
One has to write explicitly `() => next()` to achieve that. | ||
Once again since the `_` is going to be deprecated it's better to write it this way | ||
rather than `next _`. | ||
|
||
The reason for excluding nullary methods from automatic eta expansion | ||
is that Scala implicitly inserts the `()` argument, which would | ||
conflict with eta expansion. Automatic `()` insertion is | ||
[limited](../dropped-features/auto-apply.md) in Scala 3, but the fundamental ambiguity | ||
remains. | ||
|
||
[More details](eta-expansion-spec.md) |
117 changes: 117 additions & 0 deletions
117
docs/_spec/TODOreference/changed-features/implicit-conversions-spec.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
--- | ||
layout: doc-page | ||
title: "Implicit Conversions - More Details" | ||
nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/implicit-conversions-spec.html | ||
--- | ||
|
||
## Implementation | ||
|
||
An implicit conversion, or _view_, from type `S` to type `T` is | ||
defined by either: | ||
|
||
- An `implicit def` which has type `S => T` or `(=> S) => T` | ||
- An implicit value which has type `Conversion[S, T]` | ||
|
||
The standard library defines an abstract class [`Conversion`](https://scala-lang.org/api/3.x/scala/Conversion.html): | ||
|
||
```scala | ||
package scala | ||
@java.lang.FunctionalInterface | ||
abstract class Conversion[-T, +U] extends Function1[T, U]: | ||
def apply(x: T): U | ||
``` | ||
|
||
Function literals are automatically converted to `Conversion` values. | ||
|
||
Views are applied in three situations: | ||
|
||
1. If an expression `e` is of type `T`, and `T` does not conform to | ||
the expression's expected type `pt`. In this case, an implicit `v` | ||
which is applicable to `e` and whose result type conforms to `pt` | ||
is searched. The search proceeds as in the case of implicit | ||
parameters, where the implicit scope is the one of `T => pt`. If | ||
such a view is found, the expression `e` is converted to `v(e)`. | ||
1. In a selection `e.m` with `e` of type `T`, if the selector `m` does | ||
not denote an accessible member of `T`. In this case, a view `v` | ||
which is applicable to `e` and whose result contains an accessible | ||
member named `m` is searched. The search proceeds as in the case of | ||
implicit parameters, where the implicit scope is the one of `T`. If | ||
such a view is found, the selection `e.m` is converted to `v(e).m`. | ||
1. In an application `e.m(args)` with `e` of type `T`, if the selector | ||
`m` denotes some accessible member(s) of `T`, but none of these | ||
members is applicable to the arguments `args`. In this case, a view | ||
`v` which is applicable to `e` and whose result contains a method | ||
`m` which is applicable to `args` is searched. The search proceeds | ||
as in the case of implicit parameters, where the implicit scope is | ||
the one of `T`. If such a view is found, the application | ||
`e.m(args)` is converted to `v(e).m(args)`. | ||
|
||
# Differences with Scala 2 implicit conversions | ||
|
||
In Scala 2, views whose parameters are passed by-value take precedence | ||
over views whose parameters are passed by-name. This is no longer the | ||
case in Scala 3. A type error reporting the ambiguous conversions will | ||
be emitted in cases where this rule would be applied in Scala 2: | ||
|
||
```scala | ||
implicit def conv1(x: Int): String = x.toString | ||
implicit def conv2(x: => Int): String = x.toString | ||
|
||
val x: String = 0 // Compiles in Scala2 (uses `conv1`), | ||
// type error in Scala 3 because of ambiguity. | ||
``` | ||
|
||
In Scala 2, implicit values of a function type would be considered as | ||
potential views. In Scala 3, these implicit value need to have type | ||
`Conversion`: | ||
|
||
```scala | ||
// Scala 2: | ||
def foo(x: Int)(implicit conv: Int => String): String = x | ||
|
||
// Becomes with Scala 3: | ||
def foo(x: Int)(implicit conv: Conversion[Int, String]): String = x | ||
|
||
// Call site is unchanged: | ||
foo(4)(_.toString) | ||
|
||
// Scala 2: | ||
implicit val myConverter: Int => String = _.toString | ||
|
||
// Becomes with Scala 3: | ||
implicit val myConverter: Conversion[Int, String] = _.toString | ||
``` | ||
|
||
Note that implicit conversions are also affected by the [changes to implicit resolution](implicit-resolution.md) between Scala 2 and Scala 3. | ||
|
||
## Motivation for the changes | ||
|
||
The introduction of [`scala.Conversion`](https://scala-lang.org/api/3.x/scala/Conversion.html) | ||
in Scala 3 and the decision to restrict implicit values of this type to be | ||
considered as potential views comes from the desire to remove surprising | ||
behavior from the language: | ||
|
||
```scala | ||
implicit val m: Map[Int, String] = Map(1 -> "abc") | ||
|
||
val x: String = 1 // Scala 2: assigns "abc" to x | ||
// Scala 3: type error | ||
``` | ||
|
||
This snippet contains a type error. The right-hand side of `val x` | ||
does not conform to type `String`. In Scala 2, the compiler will use | ||
`m` as an implicit conversion from `Int` to `String`, whereas Scala 3 | ||
will report a type error, because `Map` isn't an instance of | ||
[`Conversion`](https://scala-lang.org/api/3.x/scala/Conversion.html). | ||
|
||
## Migration path | ||
|
||
Implicit values that are used as views should see their type changed to `Conversion`. | ||
|
||
For the migration of implicit conversions that are affected by the | ||
changes to implicit resolution, refer to the [Changes in Implicit Resolution](implicit-resolution.md) for more information. | ||
|
||
## Reference | ||
|
||
For more information about implicit resolution, see [Changes in Implicit Resolution](implicit-resolution.md). | ||
Other details are available in [PR #2065](https://github.com/lampepfl/dotty/pull/2065). |
Oops, something went wrong.