|
| 1 | +// A demonstrator how `transparent override` can be used to sepcialize inherited methods |
| 2 | +trait Iterable[+A] { |
| 3 | + def foreach(f: A => Unit): Unit |
| 4 | +} |
| 5 | +// The unconventional definition of `Range` and `UntilRange` is needed so |
| 6 | +// that `UntiRange` is recognized as a Noinits class. Our purity predictor |
| 7 | +// for classes currently bails out for arguments passed to parent classes |
| 8 | +// and for concrete `val`s. That's because the predictor runs on untyped trees |
| 9 | +// so there's no way to predict whether an expression is pure or not. |
| 10 | +// It would be nice if we could improve the predictor to work with typed trees. |
| 11 | +// The tricky bit is doing this without causing cycles. That's the price we |
| 12 | +// pay for making inlining a typelevel computation. |
| 13 | +// One possible way to do it would be to make purity a property that's computed on demand, |
| 14 | +// just like info, but evaluated later. Then we might still cause cycles, but these |
| 15 | +// would be "justified" by inlining attempts. I.e. you could avoid a cycle by |
| 16 | +// inlining less. |
| 17 | +abstract class Range extends Iterable[Int] { |
| 18 | + val start: Int |
| 19 | + val end: Int |
| 20 | + def step: Int |
| 21 | + def inclusive: Boolean |
| 22 | + def foreach(f: Int => Unit): Unit = { |
| 23 | + var idx = start |
| 24 | + while ( |
| 25 | + if (step > 0) |
| 26 | + if (inclusive) idx <= end else idx < end |
| 27 | + else |
| 28 | + if (inclusive) idx >= end else idx > end |
| 29 | + ) { |
| 30 | + f(idx) |
| 31 | + idx = idx + step |
| 32 | + } |
| 33 | + } |
| 34 | +} |
| 35 | +class UntilRange(val start: Int, val end: Int) extends Range { |
| 36 | + def step = 1 |
| 37 | + def inclusive = false |
| 38 | + transparent override def foreach(f: Int => Unit): Unit = { |
| 39 | + var idx = start |
| 40 | + while (idx < end) { |
| 41 | + f(idx) |
| 42 | + idx += 1 |
| 43 | + } |
| 44 | + } |
| 45 | +} |
| 46 | +object Test extends App { |
| 47 | + var x = 0 |
| 48 | + new UntilRange(1, 10).foreach(x += _) |
| 49 | + // Expands to: |
| 50 | + // var idx: Int = 1 |
| 51 | + // while idx < 10 do |
| 52 | + // x = x * idx |
| 53 | + // idx = idx + 1 |
| 54 | + // } |
| 55 | + |
| 56 | + class IntDeco(val x: Int) extends AnyVal { |
| 57 | + transparent def until(y: Int) = new UntilRange(x, y) |
| 58 | + } |
| 59 | + implicit transparent def intDeco(x: Int): IntDeco = new IntDeco(x) |
| 60 | + // So far, the decorator has to be an explicit def, since |
| 61 | + // we can inline only methods defined in source. So an implicit class |
| 62 | + // will not work. We can make this work by making `Desugar` smarter |
| 63 | + // and generate a transparent with pre-defined "BodyToInline" annotation. |
| 64 | + // It's doable, just extra work. |
| 65 | + |
| 66 | + (1 until 10).foreach(x += _) |
| 67 | + // expands to same as above |
| 68 | + |
| 69 | + assert(x == 90) |
| 70 | +} |
0 commit comments