|
| 1 | +--- |
| 2 | +layout: doc-page |
| 3 | +title: Inline |
| 4 | +--- |
| 5 | + |
| 6 | +`inline` is a new modifier that guarantees that a definition will be |
| 7 | +inlined at the point of use. Example: |
| 8 | + |
| 9 | + object Config { |
| 10 | + inline val logging = false |
| 11 | + } |
| 12 | + |
| 13 | + object Logger { |
| 14 | + |
| 15 | + private var indent = 0 |
| 16 | + |
| 17 | + inline def log[T](msg: => String)(op: => T): T = |
| 18 | + if (Config.logging) { |
| 19 | + println(s"${" " * indent}start $msg") |
| 20 | + indent += 1 |
| 21 | + val result = op |
| 22 | + indent -= 1 |
| 23 | + println(s"${" " * indent}$msg = $result") |
| 24 | + result |
| 25 | + } |
| 26 | + else op |
| 27 | + } |
| 28 | + |
| 29 | +The `Config` object contains a definition of an `inline` value |
| 30 | +`logging`. This means that `logging` is treated as a constant value, |
| 31 | +equivalent to its right-hand side `false`. The right-hand side of such |
| 32 | +an inline val must itself be a constant expression. Used in this way, |
| 33 | +`inline` is equivalent to Java and Scala 2's `final`. `final` meaning |
| 34 | +"constant" is still supported in Dotty, but will be phased out. |
| 35 | + |
| 36 | +The `Logger` object contains a definition of an `inline` method `log`. |
| 37 | +This method will always be inlined at the point of call. |
| 38 | + |
| 39 | +In the inlined code, an if-then-else with a constant condition will be |
| 40 | +rewritten to its then- or else-part. Here's an example: |
| 41 | + |
| 42 | + def factorial(n: BigInt): BigInt = |
| 43 | + log(s"factorial($n)") { |
| 44 | + if (n == 0) 1 |
| 45 | + else n * factorial(n - 1) |
| 46 | + } |
| 47 | + |
| 48 | +If `Config.logging == false`, this will be rewritten to |
| 49 | + |
| 50 | + def factorial(n: BigInt): BigInt = { |
| 51 | + def msg = s"factorial($n)" |
| 52 | + def op = |
| 53 | + if (n == 0) 1 |
| 54 | + else n * factorial(n - 1) |
| 55 | + op |
| 56 | + } |
| 57 | + |
| 58 | +Note that the arguments corresponding to the parameters `msg` and `op` |
| 59 | +of the inline method `log` are defined before the inlined body (which |
| 60 | +is in this case simply `op`). By-name parameters of the inlined method |
| 61 | +correspond to `def` bindings whereas by-value parameters correspond to |
| 62 | +`val` bindings. Do if `log` was defined like this: |
| 63 | + |
| 64 | + inline def log[T](msg: String)(op: => T): T = ... |
| 65 | + |
| 66 | +we'd get |
| 67 | + |
| 68 | + val msg = s"factorial($n)" |
| 69 | + |
| 70 | +instead. This behavior is designed so that calling an inline method is |
| 71 | +semantically the same as calling a normal method: By-value arguments |
| 72 | +are evaluated before the call wherea by-name arguments are evaluated |
| 73 | +each time they are referenced. As a consequence, it is often |
| 74 | +preferrable to make arguments of inline methods by-name in order to |
| 75 | +avoid unnecessary evaluations. |
| 76 | + |
| 77 | +Inline methods can be recursive. For instance, when called with a constant |
| 78 | +exponent `n`, the following method for `power` will be implemented by |
| 79 | +straight inline code without any loop or recursion. |
| 80 | + |
| 81 | + inline def power(x: Double, n: Int): Double = |
| 82 | + if (n == 0) 1.0 |
| 83 | + else if (n == 1) x |
| 84 | + else { |
| 85 | + val y = power(x, n / 2) |
| 86 | + if (n % 2 == 0) y * y else y * y * x |
| 87 | + } |
| 88 | + |
| 89 | + power(expr, 10) |
| 90 | + // translates to |
| 91 | + // |
| 92 | + // val x = expr |
| 93 | + // val y1 = x * x // ^2 |
| 94 | + // val y2 = y1 * y1 // ^4 |
| 95 | + // val y3 = y2 * x // ^5 |
| 96 | + // y3 * y3 // ^10 |
| 97 | + |
| 98 | +Parameters of inline methods can themselves be marked `inline`. This means |
| 99 | +that the argument of an inline method is itself inlined in the inlined body of |
| 100 | +the method. Using this scheme, we can define a zero-overhead `foreach` method |
| 101 | +that translates into a straightforward while loop without any indirection or |
| 102 | +overhead: |
| 103 | + |
| 104 | + inline def foreach(inline op: Int => Unit): Unit = { |
| 105 | + var i = from |
| 106 | + while (i < end) { |
| 107 | + op(i) |
| 108 | + i += 1 |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | +## Relationship to `@inline`. |
| 113 | + |
| 114 | +Existing Scala defines a `@inline` annotation which is used as a hint |
| 115 | +for the backend to inline. For most purposes, this annotation is |
| 116 | +superseded by the `inline` modifier. The modifier is more powerful |
| 117 | +than the annotation: Expansion is guarenteed instead of best effort, |
| 118 | +it happens in the fronend instead of in the backend, and it also applies |
| 119 | +to method arguments and recursive methods. |
| 120 | + |
| 121 | +Since `inline` is now a keyword, it would be a syntax error to write |
| 122 | +`@inline`. Hwoever, one can still refer to the annotation by putting |
| 123 | +it in backticks, i.e. |
| 124 | + |
| 125 | + @`inline` def ... |
| 126 | + |
| 127 | + |
| 128 | + |
| 129 | + |
| 130 | + |
0 commit comments