|
| 1 | +--- |
| 2 | +layout: blog-page |
| 3 | +title: Announcing Dotty 0.23.0-RC1 - safe initialization checks, type-level bitwise operations and more |
| 4 | +author: Anatolii Kmetiuk |
| 5 | +authorImg: /images/anatolii.png |
| 6 | +date: 2020-03-18 |
| 7 | +--- |
| 8 | + |
| 9 | +Hello! We are excited to announce 0.23.0-RC1 of Dotty. This version brings safe initialization checks, minor syntactic changes related to the context parameters, type-level bitwise operations and improvements of the metaprogramming capabilities. |
| 10 | + |
| 11 | +You can try out this version right now, from the comfort of your SBT, by visiting the [home page](https://dotty.epfl.ch/) and scrolling down to the "Create a Dotty Project" section. |
| 12 | + |
| 13 | +Alternatively, you can try this version of Scala online via [Scastie](https://scastie.scala-lang.org/). Once you're there, click "Build Settings" and set "Target" to "Dotty". |
| 14 | + |
| 15 | +Enjoy the ride🚀! |
| 16 | + |
| 17 | +<!--more--> |
| 18 | +# Cool new features |
| 19 | +## Safe initialization checks |
| 20 | +When a class is instantiated, the fields in the class body are initialized by field initializers, which could be any Scala code. Such a versatile language feature gives the programmer flexibility in defining how objects are initialized. However, such flexibility also brings complexity to ensure that we never accidentally use a field before it's initialized. Initialization errors can be difficult to spot in the presence of complex language features, such as inheritance, traits, inner classes, and aliasing. Such errors, sometimes simple sometimes subtle, require programmer efforts to debug and fix, which has been a [pain point for Scala programmers](https://contributors.scala-lang.org/t/improve-forward-reference-handling/3616) for a long time. |
| 21 | + |
| 22 | +Most programming languages do not statically check initialization safety, such as C++, Java, Kotlin, etc. |
| 23 | +Or, they check initialization safety but overly restrict how objects are initialized, like Swift. |
| 24 | +Now, Scala 3 has the best of two worlds: flexibility of initialization patterns and static check for safety. |
| 25 | + |
| 26 | +Consider the following program: |
| 27 | + |
| 28 | +```scala |
| 29 | +abstract class AbstractFile { |
| 30 | + def name: String |
| 31 | + val extension: String = name.reverse.dropWhile(_ != '.').reverse |
| 32 | +} |
| 33 | + |
| 34 | +class RemoteFile(url: String) extends AbstractFile { |
| 35 | + val localFile: String = url.hashCode + ".tmp" |
| 36 | + def name: String = localFile |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +Above, `extension` value is initialized prior to `localFile` because the fields of the parents of a class are initialized prior to the fields of the class. However, `extension` uses `localFile` during its initialization since it accesses this field from the `name` method. This scenario will lead to a `NullPointerException` on runtime when the access to uninitialized `localFile` happens. |
| 41 | + |
| 42 | + |
| 43 | +In this release, we have added an aid for the programmer to detect such mistakes automatically. If you compile the above program with the `-Ycheck-init` flag, you will get the following compile-time error: |
| 44 | + |
| 45 | +```scala |
| 46 | +-- Error: /Users/kmetiuk/Projects/scala3/pg/release/snip_4.scala:8:7 ----------- |
| 47 | +8 | val localFile: String = url.hashCode + ".tmp" |
| 48 | + | ^ |
| 49 | + |Access non-initialized field localFile. Calling trace: |
| 50 | + | -> val extension: String = name.reverse.dropWhile(_ != '.').reverse [ snip_4.scala:4 ] |
| 51 | + | -> def name: String = localFile [ snip_4.scala:9 ] |
| 52 | +1 error found |
| 53 | +``` |
| 54 | + |
| 55 | +You can learn more about the feature from the [documentation](https://dotty.epfl.ch/0.23.0-RC1/docs/reference/other-new-features/safe-initialization.html). For the discussion, see PR [#7789](https://github.com/lampepfl/dotty/pull/7789). |
| 56 | + |
| 57 | +## Bitwise Int compiletime operations |
| 58 | +In the previous release, Dotty has [received](https://dotty.epfl.ch/blog/2020/02/05/22nd-dotty-milestone-release.html#primitive-compiletime-operations-on-singleton-types) a support for type-level arithmetic operations on integers. In this release, we are extending this support by adding bitwise operations. For example: |
| 59 | + |
| 60 | +```scala |
| 61 | +import scala.compiletime.ops.int._ |
| 62 | + |
| 63 | +@main def Test = |
| 64 | + val t1: 1 << 1 = 2 |
| 65 | + val t2: 1 << 2 = 4 |
| 66 | + val t3: 1 << 3 = 8 |
| 67 | + val t4: 1 << 4 = 0 // error |
| 68 | +``` |
| 69 | + |
| 70 | +Above `t4` will fail to compile with the following error: |
| 71 | + |
| 72 | + -- [E007] Type Mismatch Error: /Users/kmetiuk/Projects/scala3/pg/release/snip_3.scala:7:20 |
| 73 | +7 | val t67: 1 << 4 = 0 // error |
| 74 | + | ^ |
| 75 | + | Found: (0 : Int) |
| 76 | + | Required: (16 : Int) |
| 77 | + |
| 78 | +You can find the list of all the supported operations in the `scala.compiletime.ops` [package](https://github.com/bishabosha/dotty/blob/e2b0de0bf70bbde5a9a92dc7fa91b36537b02a87/library/src/scala/compiletime/ops/package.scala) |
| 79 | + |
| 80 | +# Syntactic Changes |
| 81 | +## Context functions syntax improved |
| 82 | +In this release, we have done some work to improve the syntax of context functions. Now, their syntax is closer to the syntax for context parameters of methods. |
| 83 | + |
| 84 | +Previously, a context function was written as follows: |
| 85 | + |
| 86 | +```scala |
| 87 | +// OLD SYNTAX |
| 88 | +val ctxFunOld = (x: String) ?=> x.toInt |
| 89 | +``` |
| 90 | + |
| 91 | +Now, it is written as follows: |
| 92 | + |
| 93 | +```scala |
| 94 | +val ctxFunNew = (using x: String) => x.toInt |
| 95 | +``` |
| 96 | + |
| 97 | +We hope that this change will improve the readability of context functions for a person who already knows the syntax for context parameters of ordinary methods. |
| 98 | + |
| 99 | +## Drop `given` parameter syntax |
| 100 | +As part of our experimentation with the syntax of the language, we are now dropping the old syntax for context parameters. |
| 101 | + |
| 102 | +The old syntax for context parameters was as follows: |
| 103 | + |
| 104 | +```scala |
| 105 | +// OLD SYNTAX, NO LONGER SUPPORTED |
| 106 | +def f(given x: Int) = x * x |
| 107 | +``` |
| 108 | + |
| 109 | +In the previous release, it was [replaced](https://dotty.epfl.ch/blog/2020/02/05/22nd-dotty-milestone-release.html#further-improvements-to-the-context-parameters-syntax) by the new `using` syntax: |
| 110 | + |
| 111 | +```scala |
| 112 | +def f(using x: Int) = x * x |
| 113 | +``` |
| 114 | + |
| 115 | +However, both syntaxes were supported for that release for experimental purposes. Now, we are dropping the support of the old syntax in favor of the new one as we see it as a clear win over the old one. |
| 116 | + |
| 117 | +# Metaprogramming |
| 118 | +## Inline version of `summon` |
| 119 | +Inside an inline method, we often want to summon a value without declaring it as a context parameter of the method: |
| 120 | + |
| 121 | +```scala |
| 122 | +inline def lookup[X] = |
| 123 | + val x = summon[X] // error |
| 124 | + // -- Error: /Users/kmetiuk/Projects/scala3/pg/release/snip_5.scala:6:19 ---------- |
| 125 | + // 6 | val x = summon[X] |
| 126 | + // | ^ |
| 127 | + // |no implicit argument of type X was found for parameter x of method summon in object DottyPredef |
| 128 | + // 1 error found |
| 129 | + println(s"x = $x") |
| 130 | +``` |
| 131 | + |
| 132 | +The above program will give us a compile time error because it cannot find a context parameter of type `X`. This is because the `summon` function is not inline and hence the compiler needs to know that a context parameter of type `X` exists on call site of `summon` which happens to be in the body of `lookup`. Since `X` is unknown in that body, the compiler can't find the context parameter and shows an error. |
| 133 | + |
| 134 | +We have now added an inline version of `summon`: |
| 135 | + |
| 136 | +```scala |
| 137 | +import scala.compiletime.summonInline |
| 138 | + |
| 139 | +inline def lookup[X] = |
| 140 | + val x = summonInline[X] |
| 141 | + println(s"x = $x") |
| 142 | + |
| 143 | +@main def Test = |
| 144 | + given Int = 10 |
| 145 | + lookup[Int] |
| 146 | +``` |
| 147 | + |
| 148 | +`summonInline` is an inline version of `summon`. It is defined as follows: |
| 149 | + |
| 150 | +```scala |
| 151 | +inline def summonInline[T] <: T = summonFrom { |
| 152 | + case t: T => t |
| 153 | +} |
| 154 | +``` |
| 155 | + |
| 156 | +Since it is inline, the context parameter is resolved on expansion site, not the call site. The expansion site happens to be wherever `lookup` function is expanded, and there the type `X` is bound to a concrete type. |
| 157 | + |
| 158 | +## `ValueOfExpr` renamed to `Unlifted` |
| 159 | +This feature allows you to obtain the value captured in an expression using pattern matching: |
| 160 | + |
| 161 | +Macro.scala |
| 162 | + |
| 163 | +```scala |
| 164 | +import scala.quoted._ |
| 165 | + |
| 166 | +inline def square(inline x: Int): Int = ${ squareImpl('x) } |
| 167 | +def squareImpl(x: Expr[Int])(using QuoteContext): Expr[Int] = |
| 168 | + x match |
| 169 | + case Unlifted(value: Int) => Expr(value * value) |
| 170 | +``` |
| 171 | + |
| 172 | +Test.scala |
| 173 | + |
| 174 | +```scala |
| 175 | +@main def Test = |
| 176 | + println(square(10)) // println(100) |
| 177 | +``` |
| 178 | + |
| 179 | +## Extractors for quotes moved under `scala.quoted` package |
| 180 | +The metaprogramming capabilities are undergoing simplifications in this release. In particular, fewer imports are now needed. |
| 181 | + |
| 182 | +Previously, to access the extractors for expressions you had to do the `scala.quoted.matching._` import. Now, the extractors from there have been moved to `scala.quoted`. For example, you can write the following program: |
| 183 | + |
| 184 | +Macro.scala: |
| 185 | + |
| 186 | +```scala |
| 187 | +import scala.quoted._ |
| 188 | + |
| 189 | +inline def square(inline x: Int): Int = ${ squareImpl('x) } |
| 190 | +def squareImpl(xExpr: Expr[Int])(using QuoteContext): Expr[Int] = |
| 191 | + xExpr match |
| 192 | + case Const(x) => Expr(x * x) |
| 193 | +``` |
| 194 | + |
| 195 | +Test.scala: |
| 196 | + |
| 197 | +```scala |
| 198 | +@main def Test = |
| 199 | + println(square(2)) // println(4) |
| 200 | +``` |
| 201 | + |
| 202 | +Above, `Const` is an extractor that matches constants. Notice how we do not need to import anything else but `scala.quoted._` to use it. |
| 203 | + |
| 204 | +## TASTy Reflect imports simplified |
| 205 | +Previously, to access TASTy Reflect features of Dotty, you had to include an import as follows: |
| 206 | + |
| 207 | +```scala |
| 208 | +// OLD CODE |
| 209 | +import qctx.tasty.{ _, given } |
| 210 | +``` |
| 211 | + |
| 212 | +Above, `qctx` is a `QuoteContext` which is available in all the macro implementations. The `given` keyword imports all the context instances. In this particular case, it was needed to bring extension methods for ASTs in scope. |
| 213 | + |
| 214 | +With this release, the `given` part is no longer needed and the extension methods are in scope after merely importing `import qctx.tasty._`. |
| 215 | + |
| 216 | +Consider the following example: |
| 217 | + |
| 218 | +Macro.scala |
| 219 | + |
| 220 | +```scala |
| 221 | +import scala.quoted._ |
| 222 | + |
| 223 | +inline def showTree(inline x: Any): String = ${ showTreeImpl('x) } |
| 224 | +def showTreeImpl(x: Expr[Any])(using qctx: QuoteContext): Expr[String] = |
| 225 | + import qctx.tasty._ |
| 226 | + x.unseal match |
| 227 | + case Inlined(_, _, app: Apply) => |
| 228 | + val fun: Term = app.fun |
| 229 | + val args: List[Term] = app.args |
| 230 | + val res = s"Function: $fun\nApplied to: $args" |
| 231 | + Expr(res) |
| 232 | +``` |
| 233 | + |
| 234 | +Test.scala |
| 235 | + |
| 236 | +```scala |
| 237 | +@main def Test = |
| 238 | + def f(x: Int) = x * x |
| 239 | + val x = 10 |
| 240 | + println(showTree(f(x))) |
| 241 | +``` |
| 242 | + |
| 243 | +Notice how above, we are calling `app.fun` and `app.args`. `fun` and `args` are extension methods on `Apply` tree node. Previously they would not have been available unless we did `import qctx.tasty.given`. However as of this release, the above program compiles without errors. |
| 244 | + |
| 245 | +# Let us know what you think! |
| 246 | + |
| 247 | +If you have questions or any sort of feedback, feel free to send us a message on our |
| 248 | +[Gitter channel](https://gitter.im/lampepfl/dotty). If you encounter a bug, please |
| 249 | +[open an issue on GitHub](https://github.com/lampepfl/dotty/issues/new). |
| 250 | + |
| 251 | +## Contributing |
| 252 | + |
| 253 | +Thank you to all the contributors who made this release possible! |
| 254 | + |
| 255 | +According to `git shortlog -sn --no-merges 0.22.0-RC1..0.23.0-RC1` these are: |
| 256 | + |
| 257 | +``` |
| 258 | + 165 Martin Odersky |
| 259 | + 124 Nicolas Stucki |
| 260 | + 121 Liu Fengyun |
| 261 | + 45 Robert Stoll |
| 262 | + 15 Guillaume Martres |
| 263 | + 15 Anatolii |
| 264 | + 10 gzoller |
| 265 | + 8 Som Snytt |
| 266 | + 8 Stéphane Micheloud |
| 267 | + 5 Ausmarton Zarino Fernandes |
| 268 | + 5 Oron Port |
| 269 | + 3 Adam Fraser |
| 270 | + 3 Gabriele Petronella |
| 271 | + 3 Uko |
| 272 | + 3 Anatolii Kmetiuk |
| 273 | + 2 ybasket |
| 274 | + 2 Dale Wijnand |
| 275 | + 2 Dani Rey |
| 276 | + 2 Jamie Thompson |
| 277 | + 2 Olivier Blanvillain |
| 278 | + 2 Tomasz Godzik |
| 279 | + 2 Travis Brown |
| 280 | + 2 Vlastimil Dort |
| 281 | + 1 tanaka takaya |
| 282 | + 1 Miles Sabin |
| 283 | + 1 Andrew Valencik |
| 284 | + 1 bishabosha |
| 285 | + 1 fhackett |
| 286 | + 1 Lionel Parreaux |
| 287 | + 1 kenji yoshida |
| 288 | + 1 manojo |
| 289 | + 1 odersky |
| 290 | + 1 Raj Parekh |
| 291 | + 1 Sébastien Doeraene |
| 292 | + 1 xuwei-k |
| 293 | +``` |
| 294 | + |
| 295 | +If you want to get your hands dirty and contribute to Dotty, now is a good time to get involved! |
| 296 | +Head to our [Getting Started page for new contributors](https://dotty.epfl.ch/docs/contributing/getting-started.html), |
| 297 | +and have a look at some of the [good first issues](https://github.com/lampepfl/dotty/issues?q=is%3Aissue+is%3Aopen+label%3Aexp%3Anovice). |
| 298 | +They make perfect entry points into hacking on the compiler. |
| 299 | + |
| 300 | +We are looking forward to having you join the team of contributors. |
| 301 | + |
| 302 | +## Library authors: Join our community build |
| 303 | + |
| 304 | +Dotty now has a set of widely-used community libraries that are built against every nightly Dotty |
| 305 | +snapshot. Currently, this includes ScalaPB, algebra, scalatest, scopt and squants. |
| 306 | +Join our [community build](https://github.com/lampepfl/dotty-community-build) |
| 307 | +to make sure that our regression suite includes your library. |
| 308 | + |
| 309 | +[Scastie]: https://scastie.scala-lang.org/?target=dotty |
| 310 | + |
| 311 | +[@odersky]: https://github.com/odersky |
| 312 | +[@DarkDimius]: https://github.com/DarkDimius |
| 313 | +[@smarter]: https://github.com/smarter |
| 314 | +[@felixmulder]: https://github.com/felixmulder |
| 315 | +[@nicolasstucki]: https://github.com/nicolasstucki |
| 316 | +[@liufengyun]: https://github.com/liufengyun |
| 317 | +[@OlivierBlanvillain]: https://github.com/OlivierBlanvillain |
| 318 | +[@biboudis]: https://github.com/biboudis |
| 319 | +[@allanrenucci]: https://github.com/allanrenucci |
| 320 | +[@Blaisorblade]: https://github.com/Blaisorblade |
| 321 | +[@Duhemm]: https://github.com/Duhemm |
| 322 | +[@AleksanderBG]: https://github.com/AleksanderBG |
| 323 | +[@milessabin]: https://github.com/milessabin |
| 324 | +[@anatoliykmetyuk]: https://github.com/anatoliykmetyuk |
0 commit comments