Skip to content

Commit d53867c

Browse files
committed
Renaming "instance of" -> "implied for"
1 parent c0c1804 commit d53867c

33 files changed

+341
-972
lines changed

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

-1
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,6 @@ object StdNames {
483483
val notifyAll_ : N = "notifyAll"
484484
val notify_ : N = "notify"
485485
val null_ : N = "null"
486-
val of: N = "of"
487486
val ofDim: N = "ofDim"
488487
val opaque: N = "opaque"
489488
val ordinal: N = "ordinal"

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -2542,11 +2542,11 @@ object Parsers {
25422542
*/
25432543
def instanceDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
25442544
var mods1 = addMod(mods, instanceMod)
2545-
val name = if (isIdent && !isIdent(nme.of)) ident() else EmptyTermName
2545+
val name = if (isIdent) ident() else EmptyTermName
25462546
val tparams = typeParamClauseOpt(ParamOwner.Def)
25472547
val vparamss = paramClauses(ofInstance = true)
25482548
val parents =
2549-
if (isIdent(nme.of)) {
2549+
if (in.token == FOR) {
25502550
in.nextToken()
25512551
tokenSeparated(COMMA, constrApp)
25522552
}

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

+1-3
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,9 @@ object Scanners {
205205

206206
private def handleMigration(keyword: Token): Token =
207207
if (!isScala2Mode) keyword
208-
else if ( keyword == ENUM
209-
|| keyword == ERASED) treatAsIdent()
208+
else if (scala3keywords.contains(keyword)) treatAsIdent()
210209
else keyword
211210

212-
213211
private def treatAsIdent() = {
214212
testScala2Mode(i"$name is now a keyword, write `$name` instead of $name to keep it as an identifier")
215213
patch(source, Span(offset), "`")

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ object Tokens extends TokensCommon {
178178
final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
179179
final val ENUM = 62; enter(ENUM, "enum")
180180
final val ERASED = 63; enter(ERASED, "erased")
181-
final val INSTANCE = 64; enter(INSTANCE, "instance")
181+
final val INSTANCE = 64; enter(INSTANCE, "implied")
182182
final val GIVEN = 65; enter(GIVEN, "given")
183183

184184
/** special symbols */
@@ -251,5 +251,7 @@ object Tokens extends TokensCommon {
251251

252252
final val numericLitTokens: TokenSet = BitSet(INTLIT, LONGLIT, FLOATLIT, DOUBLELIT)
253253

254+
final val scala3keywords = BitSet(ENUM, ERASED, GIVEN, INSTANCE)
255+
254256
final val softModifierNames = Set(nme.inline, nme.opaque)
255257
}

compiler/src/dotty/tools/dotc/typer/Typer.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -2719,11 +2719,11 @@ class Typer extends Namer
27192719
else err.typeMismatch(tree, pt, failure)
27202720
if (ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType)
27212721
inferView(tree, pt) match {
2722-
case SearchSuccess(inferred: ExtMethodApply, _, _) =>
2723-
inferred // nothing to check or adapt for extension method applications
2724-
case SearchSuccess(inferred, _, _) =>
2725-
checkImplicitConversionUseOK(inferred.symbol, tree.posd)
2726-
readapt(inferred)(ctx.retractMode(Mode.ImplicitsEnabled))
2722+
case SearchSuccess(found: ExtMethodApply, _, _) =>
2723+
found // nothing to check or adapt for extension method applications
2724+
case SearchSuccess(found, _, _) =>
2725+
checkImplicitConversionUseOK(found.symbol, tree.posd)
2726+
readapt(found)(ctx.retractMode(Mode.ImplicitsEnabled))
27272727
case failure: SearchFailure =>
27282728
if (pt.isInstanceOf[ProtoType] && !failure.isAmbiguous)
27292729
// don't report the failure but return the tree unchanged. This

docs/docs/reference/instances/context-bounds.md renamed to docs/docs/reference/contextual/context-bounds.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ title: "Context Bounds"
55

66
## Context Bounds
77

8-
A context bound is a shorthand for expressing a common pattern of implicit parameters. For example, the `maximum` function of the last section could also have been written like this:
8+
A context bound is a shorthand for expressing a common pattern of an inferable parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this:
99
```scala
1010
def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)
1111
```
12-
A bound `: C` on a type parameter `T` of a method or class indicates an inferred parameter `given C[T]`. The inferred parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
12+
A bound like `: Ord` on a type parameter `T` of a method or class indicates an inferred parameter `given Ord[T]`. The inferred parameter(s) generated from context bounds come last in the definition of the containing method or class. E.g.,
1313
```
1414
def f[T: C1 : C2, U: C3](x: T) given (y: U, z: V): R
1515
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
layout: doc-page
3+
title: "Implied Conversions"
4+
---
5+
6+
Inferable conversions are defined by implied instances of the `scala.Conversion` class.
7+
This class is defined in package `scala` as follows:
8+
```scala
9+
abstract class Conversion[-T, +U] extends (T => U)
10+
```
11+
For example, here is an implied conversion from `String` to `Token`:
12+
```scala
13+
implied for Conversion[String, Token] {
14+
def apply(str: String): Token = new KeyWord(str)
15+
}
16+
```
17+
An implied conversion is applied automatically by the compiler in three situations:
18+
19+
1. If an expression `e` has type `T`, and `T` does not conform to the expression's expected type `S`.
20+
2. In a selection `e.m` with `e` of type `T`, but `T` defines no member `m`.
21+
3. In an application `e.m(args)` with `e` of type `T`, if ``T` does define
22+
some member(s) named `m`, but none of these members can be applied to the arguments `args`.
23+
24+
In the first case, the compiler looks in the implied scope for a an instance of
25+
`scala.Conversion` that maps an argument of type `T` to type `S`. In the second and third
26+
case, it looks for an instance of `scala.Conversion` that maps an argument of type `T`
27+
to a type that defines a member `m` which can be applied to `args` if present.
28+
If such an instance `C` is found, the expression `e` is replaced by `C.apply(e)`.
29+
30+
## Examples
31+
32+
1. The `Predef` package contains "auto-boxing" conversions that map
33+
primitive number types to subclasses of `java.lang.Number`. For instance, the
34+
conversion from `Int` to `java.lang.Integer` can be defined as follows:
35+
```scala
36+
implied int2Integer for Conversion[Int, java.lang.Integer] {
37+
def apply(x: Int) = new java.lang.Integer(x)
38+
}
39+
```
40+
41+
2. The "magnet" pattern is sometimes used to express many variants of a method. Instead of defining overloaded versions of the method, one can also let the method take one or more arguments of specially defined "magnet" types, into which various argument types can be converted. E.g.
42+
```scala
43+
object Completions {
44+
45+
// The argument "magnet" type
46+
enum CompletionArg {
47+
case Error(s: String)
48+
case Response(f: Future[HttpResponse])
49+
case Status(code: Future[StatusCode])
50+
}
51+
object CompletionArg {
52+
53+
// conversions defining the possible arguments to pass to `complete`
54+
// these always come with CompletionArg
55+
// They can be invoked explicitly, e.g.
56+
//
57+
// CompletionArg.from(statusCode)
58+
59+
implied from for Conversion[String, CompletionArg] {
60+
def apply(s: String) = CompletionArg.Error(s)
61+
}
62+
implied from for Conversion[Future[HttpResponse], CompletionArg] {
63+
def apply(f: Future[HttpResponse]) = CompletionArg.Response(f)
64+
}
65+
implied from for Conversion[Future[StatusCode], CompletionArg] {
66+
def apply(code: Future[StatusCode]) = CompletionArg.Status(code)
67+
}
68+
}
69+
import CompletionArg._
70+
71+
def complete[T](arg: CompletionArg) = arg match {
72+
case Error(s) => ...
73+
case Response(f) => ...
74+
case Status(code) => ...
75+
}
76+
}
77+
```
78+
This setup is more complicated than simple overloading of `complete`, but it can still be useful if normal overloading is not available (as in the case above, since we cannot have two overloaded methods that take `Future[...]` arguments), or if normal overloading would lead to a combinatorial explosion of variants.

docs/docs/reference/instances/derivation.md renamed to docs/docs/reference/contextual/derivation.md

+16-16
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ enum Tree[T] derives Eq, Ordering, Pickling {
1010
case Leaf(elem: T)
1111
}
1212
```
13-
The `derives` clause generates inferred instances of the `Eq`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
13+
The `derives` clause generates implied instances of the `Eq`, `Ordering`, and `Pickling` traits in the companion object `Tree`:
1414
```scala
15-
inferred [T: Eq] for Eq[Tree[T]] = Eq.derived
16-
inferred [T: Ordering] for Ordering[Tree[T]] = Ordering.derived
17-
inferred [T: Pickling] for Pickling[Tree[T]] = Pickling.derived
15+
implied [T: Eq] for Eq[Tree[T]] = Eq.derived
16+
implied [T: Ordering] for Ordering[Tree[T]] = Ordering.derived
17+
implied [T: Pickling] for Pickling[Tree[T]] = Pickling.derived
1818
```
1919

2020
### Deriving Types
@@ -47,7 +47,7 @@ These two conditions ensure that the synthesized derived instances for the trait
4747
```scala
4848
def derived[T] with Generic[T] = ...
4949
```
50-
That is, the `derived` method takes an implicit parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. A `Generic` instance is generated automatically
50+
That is, the `derived` method takes an inferable parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. A `Generic` instance is generated automatically
5151
for any types that derives a typeclass that needs it. One can also derive `Generic` alone, which means a `Generic` instance is generated without any other type class instances. E.g.:
5252
```scala
5353
sealed trait ParseResult[T] derives Generic
@@ -99,10 +99,10 @@ is represented as `T *: Unit` since there is no direct syntax for such tuples: `
9999

100100
### The Generic TypeClass
101101

102-
For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` an inferred instance of `Generic[C[T_1,...,T_n]]` that follows
102+
For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` an implied instance of `Generic[C[T_1,...,T_n]]` that follows
103103
the outline below:
104104
```scala
105-
inferred [T_1, ..., T_n] for Generic[C[T_1,...,T_n]] {
105+
implied [T_1, ..., T_n] for Generic[C[T_1,...,T_n]] {
106106
type Shape = ...
107107
...
108108
}
@@ -120,7 +120,7 @@ would produce:
120120
object Result {
121121
import scala.compiletime.Shape._
122122

123-
inferred [T, E] for Generic[Result[T, E]] {
123+
implied [T, E] for Generic[Result[T, E]] {
124124
type Shape = Cases[(
125125
Case[Ok[T], T *: Unit],
126126
Case[Err[E], E *: Unit]
@@ -311,7 +311,7 @@ The last, and in a sense most interesting part of the derivation is the comparis
311311
error("No `Eq` instance was found for $T")
312312
}
313313
```
314-
`tryEql` is an inline method that takes an element type `T` and two element values of that type as arguments. It is defined using an `inline match` that tries to find an implicit instance of `Eq[T]`. If an instance `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no instance is found
314+
`tryEql` is an inline method that takes an element type `T` and two element values of that type as arguments. It is defined using an `implicit match` that tries to find an implied instance of `Eq[T]`. If an instance `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no instance is found
315315
this signals a compilation error: the user tried a generic derivation of `Eq` for a class with an element type that does not support an `Eq` instance itself. The error is signaled by
316316
calling the `error` method defined in `scala.compiletime`.
317317

@@ -320,7 +320,7 @@ calling the `error` method defined in `scala.compiletime`.
320320
**Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eq` instance of class `Tree`.
321321

322322
```scala
323-
inferred [T] with (elemEq: Eq[T]) for Eq[Tree[T]] {
323+
implied [T] with (elemEq: Eq[T]) for Eq[Tree[T]] {
324324
def eql(x: Tree[T], y: Tree[T]): Boolean = {
325325
val ev = infer[Generic[Tree[T]]]
326326
val mx = ev.reflect(x)
@@ -339,21 +339,21 @@ inferred [T] with (elemEq: Eq[T]) for Eq[Tree[T]] {
339339
}
340340
```
341341

342-
One important difference between this approach and Scala-2 typeclass derivation frameworks such as Shapeless or Magnolia is that no automatic attempt is made to generate typeclass instances of elements recursively using the generic derivation framework. There must be an implicit instance of `Eq[T]` (which can of course be produced in turn using `Eq.derived`), or the compilation will fail. The advantage of this more restrictive approach to typeclass derivation is that it avoids uncontrolled transitive typeclass derivation by design. This keeps code sizes smaller, compile times lower, and is generally more predictable.
342+
One important difference between this approach and Scala-2 typeclass derivation frameworks such as Shapeless or Magnolia is that no automatic attempt is made to generate typeclass instances of elements recursively using the generic derivation framework. There must be an implied instance of `Eq[T]` (which can of course be produced in turn using `Eq.derived`), or the compilation will fail. The advantage of this more restrictive approach to typeclass derivation is that it avoids uncontrolled transitive typeclass derivation by design. This keeps code sizes smaller, compile times lower, and is generally more predictable.
343343

344344
### Derived Instances Elsewhere
345345

346346
Sometimes one would like to derive a typeclass instance for an ADT after the ADT is defined, without being able to change the code of the ADT itself.
347347
To do this, simply define an instance with the `derived` method of the typeclass as right-hand side. E.g, to implement `Ordering` for `Option`, define:
348348
```scala
349-
inferred [T: Ordering]: Ordering[Option[T]] = Ordering.derived
349+
implied [T: Ordering]: Ordering[Option[T]] = Ordering.derived
350350
```
351-
Usually, the `Ordering.derived` clause has an implicit parameter of type
351+
Usually, the `Ordering.derived` clause has an inferable parameter of type
352352
`Generic[Option[T]]`. Since the `Option` trait has a `derives` clause,
353-
the necessary inferred instance is already present in the companion object of `Option`.
354-
If the ADT in question does not have a `derives` clause, an implicit `Generic` instance
353+
the necessary implied instance is already present in the companion object of `Option`.
354+
If the ADT in question does not have a `derives` clause, an implied `Generic` instance
355355
would still be synthesized by the compiler at the point where `derived` is called.
356-
This is similar to the situation with type tags or class tags: If no implicit instance
356+
This is similar to the situation with type tags or class tags: If no implied instance
357357
is found, the compiler will synthesize one.
358358

359359
### Syntax

docs/docs/reference/instances/extension-methods.md renamed to docs/docs/reference/contextual/extension-methods.md

+7-8
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ When is an extension method applicable? There are two possibilities.
3636

3737
- An extension method is applicable if it is visible under a simple name, by being defined
3838
or inherited or imported in a scope enclosing the application.
39-
- An extension method is applicable if it is a member of an eligible implicit value at the point of the application.
39+
- An extension method is applicable if it is a member of some implied instance at the point of the application.
4040

4141
As an example, consider an extension method `longestStrings` on `String` defined in a trait `StringSeqOps`.
4242

@@ -48,16 +48,15 @@ trait StringSeqOps {
4848
}
4949
}
5050
```
51-
We can make the extension method available by defining an implicit instance of `StringSeqOps`, like this:
51+
We can make the extension method available by defining an implied instance of `StringSeqOps`, like this:
5252
```scala
53-
instance StringSeqOps1 of StringSeqOps
53+
implied ops1 for StringSeqOps
5454
```
5555
Then
5656
```scala
5757
List("here", "is", "a", "list").longestStrings
5858
```
59-
is legal everywhere `StringSeqOps1` is available as an implicit value. Alternatively, we can define `longestStrings`
60-
as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method.
59+
is legal everywhere `ops1` is available as an implied instance. Alternatively, we can define `longestStrings` as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method.
6160

6261
```scala
6362
object ops2 extends StringSeqOps
@@ -70,14 +69,14 @@ Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type ar
7069
and where `T` is the expected type. The following two rewritings are tried in order:
7170

7271
1. The selection is rewritten to `m[Ts](e)`.
73-
2. If the first rewriting does not typecheck with expected type `T`, and there is an implicit value `i`
74-
in either the current scope or in the implicit scope of `T`, and `i` defines an extension
72+
2. If the first rewriting does not typecheck with expected type `T`, and there is an implied instance `i`
73+
in either the current scope or in the implied scope of `T`, and `i` defines an extension
7574
method named `m`, then selection is expanded to `i.m[Ts](e)`.
7675
This second rewriting is attempted at the time where the compiler also tries an implicit conversion
7776
from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results.
7877

7978
So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided
80-
`circle` has type `Circle` and `CircleOps` is an eligible implicit (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`).
79+
`circle` has type `Circle` and `CircleOps` is an eligible implied instance (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`).
8180

8281
## Instances for Extension Methods
8382

docs/docs/reference/instances/implicit-conversions.md renamed to docs/docs/reference/contextual/implicit-conversions.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ layout: doc-page
33
title: "Implicit Conversions"
44
---
55

6-
Implicit conversions are defined by inferred instances of the `scala.Conversion` class.
6+
Inferable conversions are defined by implied instances of the `scala.Conversion` class.
77
This class is defined in package `scala` as follows:
88
```scala
99
abstract class Conversion[-T, +U] extends (T => U)
1010
```
1111
For example, here is an implicit conversion from `String` to `Token`:
1212
```scala
13-
inferred for Conversion[String, Token] {
13+
implied for Conversion[String, Token] {
1414
def apply(str: String): Token = new KeyWord(str)
1515
}
1616
```
@@ -33,7 +33,7 @@ If such an instance `C` is found, the expression `e` is replaced by `C.apply(e)`
3333
primitive number types to subclasses of `java.lang.Number`. For instance, the
3434
conversion from `Int` to `java.lang.Integer` can be defined as follows:
3535
```scala
36-
inferred int2Integer for Conversion[Int, java.lang.Integer] {
36+
implied int2Integer for Conversion[Int, java.lang.Integer] {
3737
def apply(x: Int) = new java.lang.Integer(x)
3838
}
3939
```
@@ -56,13 +56,13 @@ object Completions {
5656
//
5757
// CompletionArg.from(statusCode)
5858

59-
inferred from for Conversion[String, CompletionArg] {
59+
implied from for Conversion[String, CompletionArg] {
6060
def apply(s: String) = CompletionArg.Error(s)
6161
}
62-
inferred from for Conversion[Future[HttpResponse], CompletionArg] {
62+
implied from for Conversion[Future[HttpResponse], CompletionArg] {
6363
def apply(f: Future[HttpResponse]) = CompletionArg.Response(f)
6464
}
65-
inferred from for Conversion[Future[StatusCode], CompletionArg] {
65+
implied from for Conversion[Future[StatusCode], CompletionArg] {
6666
def apply(code: Future[StatusCode]) = CompletionArg.Status(code)
6767
}
6868
}

0 commit comments

Comments
 (0)