Skip to content

Commit babefd8

Browse files
committed
wip
1 parent 8c8267a commit babefd8

File tree

8 files changed

+375
-215
lines changed

8 files changed

+375
-215
lines changed

docs/docs/reference/features-classification.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ It's worth noting that macros were never included in the Scala 2 language specif
170170
To enable porting most uses of macros, we are experimenting with the advanced language constructs listed below. These designs are more provisional than the rest of the proposed language constructs for Scala 3.0. There might still be some changes until the final release. Stabilizing the feature set needed for meta programming is our first priority.
171171

172172
- [Match Types](https://dotty.epfl.ch/docs/reference/new-types/match-types.html) allow computation on types.
173-
- [Inline](https://dotty.epfl.ch/docs/reference/other-new-features/inline.html) provides
173+
- [Inline](https://dotty.epfl.ch/docs/reference/metaprogramming/inline.html) provides
174174
by itself a straightforward implementation of some simple macros and is at the same time an essential building block for the implementation of complex macros.
175-
- [Quotes and Splices](https://dotty.epfl.ch/docs/reference/other-new-features/principled-meta-programming.html) provide a principled way to express macros and staging with a unified set of abstractions.
175+
- [Quotes and Splices](https://dotty.epfl.ch/docs/reference/metaprogramming/macros.html) provide a principled way to express macros and staging with a unified set of abstractions.
176176
- [Typeclass derivation](https://dotty.epfl.ch/docs/reference/contextual/derivation.html) provides an in-language implementation of the `Gen` macro in Shapeless and other foundational libraries. The new implementation is more robust, efficient and easier to use than the macro.
177177
- [Implicit by-name parameters](https://dotty.epfl.ch/docs/reference/contextual/inferable-by-name-parameters.html) provide a more robust in-language implementation of the `Lazy` macro in Shapeless.
178178
- [Erased Terms](https://dotty.epfl.ch/docs/reference/other-new-features/erased-terms.html) provide a general mechanism for compile-time-only computations.

docs/docs/reference/metaprogramming/inline.md

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ object Logger {
3030
}
3131
```
3232

33-
The `Config` object contains a definition of the **inline value**
34-
`logging`. This means that `logging` is treated as a _constant value_,
35-
equivalent to its right-hand side `false`. The right-hand side of such
36-
an `inline val` must itself be a [constant expression](#the-definition-of-constant-expression).
37-
Used in this way, `inline` is equivalent to Java and Scala 2's `final`. `final` meaning
33+
The `Config` object contains a definition of the **inline value** `logging`.
34+
This means that `logging` is treated as a _constant value_, equivalent to its
35+
right-hand side `false`. The right-hand side of such an `inline val` must itself
36+
be a [constant expression](#the-definition-of-constant-expression). Used in this
37+
way, `inline` is equivalent to Java and Scala 2's `final`. `final` meaning
3838
"constant" is still supported in Dotty, but will be phased out.
3939

4040
The `Logger` object contains a definition of the **inline method** `log`.
@@ -106,16 +106,16 @@ val msg = s"factorial($n)"
106106
```
107107

108108
instead. This behavior is designed so that calling an inline method is
109-
semantically the same as calling a normal method: By-value arguments
110-
are evaluated before the call whereas by-name arguments are evaluated
111-
each time they are referenced. As a consequence, it is often
112-
preferable to make arguments of inline methods by-name in order to
113-
avoid unnecessary evaluations. Additionally, in the code above, our
114-
goal is to print the result after the evaluation of `op`. Imagine, if we were
115-
printing the duration of the evaluation between the two prints.
116-
117-
For instance, here is how we can define a zero-overhead `foreach` method
118-
that translates into a straightforward while loop without any indirection or
109+
semantically the same as calling a normal method: By-value arguments are
110+
evaluated before the call whereas by-name arguments are evaluated each time they
111+
are referenced. As a consequence, it is often preferable to make arguments of
112+
inline methods by-name in order to avoid unnecessary evaluations. Additionally,
113+
in the code above, our goal is to print the result after the evaluation of `op`.
114+
Imagine, if we were printing the duration of the evaluation between the two
115+
prints.
116+
117+
For instance, here is how we can define a zero-overhead `foreach` method that
118+
translates into a straightforward while loop without any indirection or
119119
overhead:
120120

121121
```scala
@@ -128,7 +128,8 @@ inline def foreach(op: => Int => Unit): Unit = {
128128
}
129129
```
130130

131-
By contrast, if `op` is a call-by-value parameter, it would be evaluated separately as a closure.
131+
By contrast, if `op` is a call-by-value parameter, it would be evaluated
132+
separately as a closure.
132133

133134
Inline methods can be recursive. For instance, when called with a constant
134135
exponent `n`, the following method for `power` will be implemented by
@@ -205,10 +206,6 @@ constant expressions in the sense defined by the [SLS §
205206
including "platform-specific" extensions such as constant folding of pure
206207
numeric computations.
207208

208-
#### Implicit Match
209-
210-
https://github.com/lampepfl/dotty/pull/5392/files
211-
212209
### Specializing Inline (Whitebox)
213210

214211
Inline methods support the `<: T` return syntax. This means that the return type
@@ -353,7 +350,6 @@ can safely use it to scrutinize its return type (`S[S[Z]]` in this case).
353350
`constvalue` is a function that produces the constant value represented by a
354351
type.
355352

356-
357353
```scala
358354
inline def toIntC[N] <: Int =
359355
inline scala.compiletime.constValue[N] match {
@@ -364,8 +360,89 @@ inline def toIntC[N] <: Int =
364360
final val ctwo = toIntC[2]
365361
```
366362

363+
#### Implicit Match
364+
365+
It is foreseen that many areas of typelevel programming can be done with rewrite
366+
methods instead of implicits. But sometimes implicits are unavoidable. The
367+
problem so far was that the Prolog-like programming style of implicit search
368+
becomes viral: Once some construct depends on implicit search it has to be
369+
written as a logic program itself. Consider for instance the problem of creating
370+
a `TreeSet[T]` or a `HashSet[T]` depending on whether `T` has an `Ordering` or
371+
not. We can create a set of implicit definitions like this:
372+
373+
```scala
374+
trait SetFor[T, S <: Set[T]]
375+
class LowPriority {
376+
implicit def hashSetFor[T]: SetFor[T, HashSet[T]] = ...
377+
}
378+
object SetsFor extends LowPriority {
379+
implicit def treeSetFor[T: Ordering]: SetFor[T, TreeSet[T]] = ...
380+
}
381+
```
382+
383+
Clearly, this is not pretty. Besides all the usual indirection of implicit
384+
search, we face the problem of rule prioritization where we have to ensure that
385+
`treeSetFor` takes priority over `hashSetFor` if the element type has an
386+
ordering. This is solved (clumsily) by putting `hashSetFor` in a superclass
387+
`LowPriority` of the object `SetsFor` where `treeSetFor` is defined. Maybe the
388+
boilerplate would still be acceptable if the crufty code could be contained.
389+
However, this is not the case. Every user of the abstraction has to be
390+
parameterized itself with a `SetFor` implicit. Considering the simple task _"I
391+
want a `TreeSet[T]` if `T` has an ordering and a `HashSet[T]` otherwise"_, this
392+
seems like a lot of ceremony.
393+
394+
There are some proposals to improve the situation in specific areas, for
395+
instance by allowing more elaborate schemes to specify priorities. But they all
396+
keep the viral nature of implicit search programs based on logic programming.
397+
398+
By contrast, the new `implicit match` construct makes implicit search available
399+
in a functional context. To solve the problem of creating the right set, one
400+
would use it as follows:
401+
```scala
402+
inline def setFor[T]: Set[T] = implicit match {
403+
case ord: Ordering[T] => new TreeSet[T]
404+
case _ => new HashSet[T]
405+
}
406+
```
407+
An implicit match uses the `implicit` keyword in the place of the scrutinee. Its
408+
patterns are type ascriptions of the form `identifier : Type`.
409+
410+
Patterns are tried in sequence. The first case with a pattern `x: T` such that
411+
an implicit value of type `T` can be summoned is chosen. The variable `x` is
412+
then bound to the implicit value for the remainder of the case. It can in turn
413+
be used as an implicit in the right hand side of the case. It is an error if one
414+
of the tested patterns gives rise to an ambiguous implicit search.
415+
416+
An implicit matches is considered to be a special kind of a inline match. This
417+
means it can only occur in the body of an inline method, and it must be reduced
418+
at compile time.
419+
420+
Consequently, if we summon an `Ordering[String]` the code above will return a
421+
new instance of `TreeSet[String]`.
422+
423+
```scala
424+
the[Ordering[String]]
425+
426+
println(setFor[String].getClass) // prints class scala.collection.immutable.TreeSet
427+
```
428+
429+
**Note** implicit matches can raise ambiguity errors. Consider the following
430+
code with two implicit values in scope of type `A`. The single pattern match
431+
case of the implicit match with type ascription of an `A` raises the ambiguity
432+
error.
433+
434+
```scala
435+
class A
436+
implicit val a1: A = new A
437+
implicit val a2: A = new A
438+
439+
inline def f: Any = implicit match {
440+
case _: A => ??? // error: ambiguous implicits
441+
}
442+
```
367443

368444
### Reference
369445

370-
For more info, see [PR #4927](https://github.com/lampepfl/dotty/pull/4768), which explains how
371-
inline methods can be used for typelevel programming and code specialization.
446+
For more info, see [PR #4927](https://github.com/lampepfl/dotty/pull/4768),
447+
which explains how inline methods can be used for typelevel programming and code
448+
specialization.

0 commit comments

Comments
 (0)