Skip to content

Commit 53ac2da

Browse files
committed
Update doc page and move into annotation to annotation package
1 parent 6fa3ce5 commit 53ac2da

File tree

7 files changed

+94
-24
lines changed

7 files changed

+94
-24
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1015,7 +1015,7 @@ class Definitions {
10151015
@tu lazy val ImplicitAmbiguousAnnot: ClassSymbol = requiredClass("scala.annotation.implicitAmbiguous")
10161016
@tu lazy val ImplicitNotFoundAnnot: ClassSymbol = requiredClass("scala.annotation.implicitNotFound")
10171017
@tu lazy val InlineParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InlineParam")
1018-
@tu lazy val IntoAnnot: ClassSymbol = requiredClass("scala.annotation.internal.into")
1018+
@tu lazy val IntoAnnot: ClassSymbol = requiredClass("scala.annotation.into")
10191019
@tu lazy val IntoParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into")
10201020
@tu lazy val ErasedParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.ErasedParam")
10211021
@tu lazy val MainAnnot: ClassSymbol = requiredClass("scala.main")

docs/_docs/reference/experimental/into-modifier.md

+53-14
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ The `into` modifier on the type of `elems` means that implicit conversions can b
3232
`into` also allows conversions on the results of function arguments. For instance, consider the new proposed signature of the `flatMap` method on `List[A]`:
3333

3434
```scala
35-
def flatMap[B](f: into A => IterableOnce[B]): List[B]
35+
def flatMap[B](f: A => into IterableOnce[B]): List[B]
3636
```
37-
This allows a conversion of the actual argument to the function type `A => IterableOnce[B]`. Crucially, it also allows that conversion to be applied to
38-
the function result. So the following would work:
37+
This accepts all actual arguments `f` that, when applied to an `A`, give a result
38+
that is convertible to `IterableOnce[B]`. So the following would work:
3939
```scala
4040
scala> val xs = List(1, 2, 3)
4141
scala> xs.flatMap(x => x.toString * x)
@@ -49,7 +49,7 @@ When applied to a vararg parameter, `into` allows a conversion on each argument
4949
number of `IterableOnce[Char]` arguments, and also allows implicit conversions into `IterableOnce[Char]`:
5050

5151
```scala
52-
def concatAll(xss: into IterableOnce[Char]*): List[Char] =
52+
def concatAll(xss: (into IterableOnce[Char])*): List[Char] =
5353
xss.foldLeft(List[Char]())(_ ++ _)
5454
```
5555
Here, the call
@@ -58,24 +58,63 @@ concatAll(List('a'), "bc", Array('d', 'e'))
5858
```
5959
would apply two _different_ implicit conversions: the conversion from `String` to `Iterable[Char]` gets applied to the second argument and the conversion from `Array[Char]` to `Iterable[Char]` gets applied to the third argument.
6060

61+
Note that a vararg parameter type with into modifiers needs to be put in parentheses, as is shown in the example above. This is to make the precedence clear: each element of the argument sequence is converted by itself.
62+
6163
## Retrofitting Scala 2 libraries
6264

63-
A new annotation `allowConversions` has the same effect as an `into` modifier. It is defined as an `@experimental` class in package `scala.annotation`. It is intended to be used for retrofitting Scala 2 library code so that Scala 3 conversions can be applied to arguments without language imports. For instance, the definitions of
65+
There is also an annotation `@into` in the `scala.annotation` package that has
66+
has the same effect as an `into` modifier. It is intended to be used for retrofitting Scala 2 library code so that Scala 3 conversions can be applied to arguments without language imports. For instance, the definitions of
6467
`++` and `flatMap` in the Scala 2.13 `List` class could be retrofitted as follows.
6568
```scala
66-
def ++ (@allowConversions elems: IterableOnce[A]): List[A]
67-
def flatMap[B](@allowConversions f: A => IterableOnce[B]): List[B]
69+
def ++ (elems: IterableOnce[A] @into): List[A]
70+
def flatMap[B](f: A => IterableOnce[B] @into): List[B]
71+
```
72+
For Scala 3 code, the `into` modifier is preferred, because it adheres to the principle that annotations should not influence typing and type inference in Scala.
73+
74+
## Restrictions
75+
76+
The `into` modifier is only allowed in the types method parameters. It can be given either for the whole type, or some result type of a top-level function type, but not anywhere else. The `into` modifier does not propagate outside the method. In particular, a partially applied method does not propagate `into` modifiers to its result.
77+
78+
**Example:**
79+
80+
Say we have
81+
```scala
82+
def f(x: Int)(y: into Text): Unit
83+
```
84+
then
85+
```scala
86+
f(3) : Text => Unit
87+
```
88+
Note the `into` modifier is not longer present on the type of `f(3)`. Therefore, follow-on arguments to `f(3)` do not allow implicit conversions. Generally it is not possible to
89+
define function types that allow implicit conversions on their arguments, but it is possible to define SAM types that allow conversions. E.g.
90+
```scala
91+
trait ConvArg:
92+
def apply(x: into Text): Unit
93+
94+
val x: ConvArg = f(3)(_)
95+
```
96+
97+
Note this is similar to the way vararg parameters are handled in Scala. If we have
98+
```scala
99+
def g(x: Int)(y: Int*): Unit
100+
```
101+
then
102+
```scala
103+
g(4) : Seq[Int] => Unit
68104
```
69-
For Scala 3 code, the `into` modifier is preferred. First, because it is shorter,
70-
and second, because it adheres to the principle that annotations should not influence
71-
typing and type inference in Scala.
105+
Observe that the vararg annotation also got dropped in the result type of `g(4)`.
72106

73107
## Syntax changes
74108

75109
The addition to the grammar is:
76110
```
77-
ParamType ::= [‘=>’] ParamValueType
78-
ParamValueType ::= [‘into‘] ExactParamType
79-
ExactParamType ::= Type [‘*’]
111+
ParamType ::= [‘=>’] ParamValueType
112+
ParamValueType ::= Type [‘*’]
113+
| IntoType
114+
| ‘(’ IntoType ‘)’ ‘*’
115+
IntoType ::= [‘into’] IntoTargetType
116+
| ‘(’ IntoType ‘)’
117+
IntoTargetType ::= Type
118+
| FunTypeArgs (‘=>’ | ‘?=>’) IntoType
80119
```
81-
As the grammar shows, `into` can only applied to the type of a parameter; it is illegal in other positions.
120+
As the grammar shows, `into` can only applied to the type of a parameter; it is illegal in other positions. Also, `into` modifiers in vararg types have to be enclosed in parentheses.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package scala.annotation.internal
2+
import annotation.experimental
3+
4+
/** An internal annotation on (part of) a parameter type that allows implicit conversions
5+
* for its arguments. The publicly visible `into` annotation in the parent package
6+
* `annotation` gets mapped to `$into` by the compiler in all places where
7+
* conversions should be allowed. The reason for the split into two annotations
8+
* is that `annotation.into` is given in source code and may propagate in unspecified
9+
* ways through type inference. By contrast `$into` is constrained to be occur only
10+
* on parameters of method types. This makes implicit conversion insertion
11+
* predictable and independent of the un-specified aspects of type inference.
12+
*/
13+
@experimental
14+
class $into() extends annotation.StaticAnnotation
15+
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
package scala.annotation.internal
1+
package scala.annotation
22
import annotation.experimental
33

44
/** An annotation on (part of) a parameter type that allows implicit conversions
55
* for its arguments. The `into` modifier on parameter types in Scala 3 is
6-
* mapped to this annotation. We can also install a more generally accessible
7-
* alias so that Scala 2 libraries can use the feature.
6+
* mapped to this annotation. The annotation is intended to be used directly in
7+
* Scala 2 sources only. For Scala 3, the `into` modifier should be preferred.
88
*/
99
@experimental
1010
class into() extends annotation.StaticAnnotation
11-
12-
@experimental
13-
class $into() extends annotation.StaticAnnotation
14-

tests/new/test.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
import language.experimental.captureChecking
2+
13
object Test:
2-
def f: Any = 1
4+
val x = caps.cap

tests/pos/into-sam.scala

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
//> using options -feature -Xfatal-warnings
3+
4+
import language.experimental.into
5+
6+
class Text(val str: String)
7+
8+
given Conversion[String, Text] = Text(_)
9+
object Test:
10+
def f(x: Int)(y: into Text): Unit = ()
11+
val _: Text => Unit = f(3)
12+
13+
trait ConvArg:
14+
def apply(x: into Text): Unit
15+
16+
val x: ConvArg = f(3)(_)
17+
18+
x("abc")

tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ val experimentalDefinitionInLibrary = Set(
5757
"scala.caps$",
5858

5959
//// New feature: into
60-
"scala.annotation.internal.into",
60+
"scala.annotation.into",
6161
"scala.annotation.internal.$into",
6262

6363
//// New feature: @publicInBinary

0 commit comments

Comments
 (0)