Skip to content

Commit

Permalink
Add copy of reference as TODO
Browse files Browse the repository at this point in the history
reference taken from scala@7919cb3
  • Loading branch information
Sporarum committed Feb 6, 2023
1 parent 87f1e82 commit 34b836b
Show file tree
Hide file tree
Showing 121 changed files with 15,330 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/_spec/TODOreference/changed-features/changed-features.md
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 docs/_spec/TODOreference/changed-features/compiler-plugins.md
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 docs/_spec/TODOreference/changed-features/eta-expansion-spec.md
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 docs/_spec/TODOreference/changed-features/eta-expansion.md
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 docs/_spec/TODOreference/changed-features/implicit-conversions-spec.md
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).
Loading

0 comments on commit 34b836b

Please sign in to comment.