|
| 1 | +--- |
| 2 | +layout: blog-page |
| 3 | +title: Announcing Dotty 0.22.0-RC1 - syntactic enhancements, type-level arithmetics and more |
| 4 | +author: Anatolii Kmetiuk |
| 5 | +authorImg: /images/anatolii.png |
| 6 | +date: 2020-02-03 |
| 7 | +--- |
| 8 | + |
| 9 | +Hello! We are excited to announce 0.22.0-RC1 of Dotty. This version brings syntactic enhancements for extension methods and contextual parameters, as well as the kind projector syntax. Other notable changes include type-level arithmetics, changes to the `inline` parameters semantics and suggestions on missing contextual parameters. |
| 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. Enjoy the ride🚀! |
| 12 | + |
| 13 | +<!--more--> |
| 14 | +# New syntax for collective extension methods |
| 15 | +Extension methods have been present in Dotty for a while. They present an idiomatic way to extend types with methods after these types are defined. For example: |
| 16 | + |
| 17 | +```scala |
| 18 | +def (x: Int) toPower (n: Int): Int = |
| 19 | + @annotation.tailrec def loop(accum: Int, power: Int): Int = |
| 20 | + if power == 0 then accum |
| 21 | + else if power > 0 then loop(accum * x, power - 1) |
| 22 | + else throw IllegalArgumentException("This operation only supports positive powers") |
| 23 | + loop(1, n) |
| 24 | + println(s"3^3 = ${3.toPower(3)}") // 3^3 = 27 |
| 25 | +``` |
| 26 | + |
| 27 | +However, when one wants to define multiple extension methods for a type, a lot of boilerplate manifests: |
| 28 | + |
| 29 | +```scala |
| 30 | +def (x: Int) toPower (n: Int): Int = ??? |
| 31 | +def (x: Int) squared = ??? |
| 32 | +def (x: Int) asBinaryString = ??? |
| 33 | +``` |
| 34 | + |
| 35 | +The type in question and the name of its parameter, `(x: Int)`, repeat. |
| 36 | + |
| 37 | +This boilerplate was the motivation to introduce collective extension methods. For a while, we were experimenting with looking at these through the lens of the `given` mechanism. We have tried out an idea of making these methods belong to an object visible in the `given` scope and, if such an object is present in the `given` scope, its extension methods are also automatically usable. |
| 38 | + |
| 39 | +However, `given` instances are about *types* and the collective extension methods describe *parameters* of extension methods. Hence, in this release we introduce a new syntax for the collective extension methods: |
| 40 | + |
| 41 | +```scala |
| 42 | +extension listOps on [T](xs: List[T]) { |
| 43 | + def second = xs.tail.head |
| 44 | + def third: T = xs.tail.tail.head |
| 45 | +} |
| 46 | + |
| 47 | +val list = List(1, 2, 3) |
| 48 | +println(s"Second: ${list.second}") // 2 |
| 49 | +println(s"Third: ${list.third}") // 3 |
| 50 | +``` |
| 51 | + |
| 52 | +This syntax is a completely separate one from the `given` syntax and hence is aimed to bring more clarity and disentangle the two different concepts. |
| 53 | + |
| 54 | +For the discussion, see [PR #7917](https://github.com/lampepfl/dotty/pull/7917). For more information on how to use extension methods in general and collective extension methods in particular, see the [documentation](https://dotty.epfl.ch/docs/reference/contextual/extension-methods.html). |
| 55 | + |
| 56 | +# Kind projector syntax support |
| 57 | +[Kind projector](https://github.com/typelevel/kind-projector) is a popular compiler plugin for Scala 2. It is especially useful in the context of purely functional programming and typeclass derivation – everywhere where you need to work extensively with types. |
| 58 | + |
| 59 | +As of this release, a subset of the kind projector syntax is now supported in Dotty. Credits for this contribution go to [Travis Brown](https://github.com/travisbrown). |
| 60 | + |
| 61 | +To enable it, you need to run the compiler with the `-Ykind-projector` flag. You can e.g. write the following: |
| 62 | + |
| 63 | +```scala |
| 64 | +// Fix #7139: Implement kind-projector compatibility #7775 |
| 65 | +// With -Ykind-projector |
| 66 | + |
| 67 | +trait Functor[F[_]] |
| 68 | + def map[A, B](fa: F[A], f: A => B): F[B] |
| 69 | + |
| 70 | +object eitherFunctor extends Functor[Either[Int, *]] |
| 71 | + def map[A, B](fa: Either[Int, A], f: A => B): Either[Int, B] = fa match |
| 72 | + case Right(x) => Right(f(x)) |
| 73 | + case Left(x) => Left(x) |
| 74 | + |
| 75 | +object functionFunctor extends Functor[Int => *] |
| 76 | + def map[A, B](fa: Int => A, f: A => B): Int => B = |
| 77 | + fa andThen f |
| 78 | + |
| 79 | +object tupleFunctor extends Functor[λ[x => (x, x)]] |
| 80 | + def map[A, B](fa: (A, A), f: A => B): (B, B) = fa match |
| 81 | + case (a1, a2) => (f(a1), f(a2)) |
| 82 | + |
| 83 | +@main def Test = |
| 84 | + val tpl = (1, 2) |
| 85 | + val squared = tupleFunctor.map(tpl, a => a * a) |
| 86 | + println(squared) // (1,4) |
| 87 | +``` |
| 88 | + |
| 89 | +For the discussion, see [PR #7775](https://github.com/lampepfl/dotty/pull/7775). Also see the GitHub [repository](https://github.com/typelevel/kind-projector) of the kind projector Scala 2 plugin for more context. |
| 90 | + |
| 91 | +# Further improvements to the contextual parameters syntax |
| 92 | +Scala 3 contextual parameters are successors of Scala 2 implicits. In Scala 2, they proved useful for a wide range of applications including purely functional programming, dependency injection, type class derivation, type-level programming. Because their apparent value, one of the priorities in Scala 3 for us is to improve the conceptual framework behind them. |
| 93 | + |
| 94 | +The state of contextual parameters before this release heavily employed the `given` keyword. For example: |
| 95 | + |
| 96 | +```scala |
| 97 | +// OLD SYNTAX BELOW |
| 98 | +given String = "10" |
| 99 | +given (given str: String) : Int = str.toInt |
| 100 | +def f(x: Int)(given y: Int) = x * y |
| 101 | +``` |
| 102 | + |
| 103 | +The above is a suboptimal solution, however. The feedback we received from the community suggested that many people felt like the `given` keyword was overused, similarly to the `implict` keyword in Scala 2. This overuse is one of the things we'd like to avoid in Scala 3. It leads, for example, to situations like `given (given ...)` which are not nice to read. |
| 104 | + |
| 105 | +For this release, we have changed the syntax for the contextual parameters. The above snippet now becomes: |
| 106 | + |
| 107 | +```scala |
| 108 | +given String = "10" |
| 109 | +given with (str: String) : Int = str.toInt |
| 110 | +def f(x: Int) with (y: Int) = x * y |
| 111 | +``` |
| 112 | + |
| 113 | +On the call site, the syntax for explicitly specifying the contextual parameters is now: |
| 114 | + |
| 115 | +```scala |
| 116 | +f(2).with(20) |
| 117 | +``` |
| 118 | + |
| 119 | +As opposed to the previous: |
| 120 | + |
| 121 | +```scala |
| 122 | +// OLD SYNTAX BELOW |
| 123 | +f(2)(given 20) |
| 124 | +``` |
| 125 | + |
| 126 | +For the time being, the change is experimental and the old syntax is also supported. For the discussion, see [PR #8017](https://github.com/lampepfl/dotty/pull/8017). You can browse the documentation concerning the new syntax [here](https://dotty.epfl.ch/docs/reference/contextual/motivation-new.html). |
| 127 | + |
| 128 | +# Semantics of inline parameters changed |
| 129 | +Inline parameters is a metaprogramming feature of Dotty which allows to splice the body of the parameter on its call site. Previously, inline parameters to methods were required to be known on compile time. With this release, this constraint has been relaxed. The following: |
| 130 | + |
| 131 | +```scala |
| 132 | +inline def sumTwice(a: Int, b: =>Int, inline c: Int) = a + a + b + b + c + c |
| 133 | +sumTwice(f(), g(), h()) |
| 134 | +``` |
| 135 | + |
| 136 | +Translates to: |
| 137 | + |
| 138 | +```scala |
| 139 | + val a = f() |
| 140 | + def b = g() |
| 141 | + a + a + b + b + h() + h() |
| 142 | +``` |
| 143 | + |
| 144 | +This change was introduced by [PR #8060](https://github.com/lampepfl/dotty/pull/8060/). For more information about the inline capability of Dotty, see [documentation](https://dotty.epfl.ch/docs/reference/metaprogramming/inline.html). |
| 145 | + |
| 146 | +# Primitive compiletime operations on singleton types |
| 147 | +Contributed by [Maxime Kajaer](https://github.com/MaximeKjaer), this release brings along type-level arithmetics: |
| 148 | + |
| 149 | +```scala |
| 150 | +import scala.compiletime.ops.int._ |
| 151 | + |
| 152 | +val x: 2 + 3 = 5 // OK |
| 153 | +val y: 3 * 4 + 1 = 12 // error |
| 154 | +``` |
| 155 | + |
| 156 | +The compile-time error above will say: |
| 157 | + |
| 158 | +```scala |
| 159 | +4 |val y: 3 * 4 + 1 = 12 |
| 160 | + | ^^ |
| 161 | + | Found: (12 : Int) |
| 162 | + | Required: (13 : Int) |
| 163 | +``` |
| 164 | + |
| 165 | +This feature is particularly useful for data science applications. In data science, it is very easy to make a linear algebra mistake, multiply matrices of wrong dimensions and get a runtime error – sometimes after a few hours of running the model. Hence compile-time verification of the models has a great potential for saving time. With such a type-level arithmetics, Scala becomes well-positioned to implement such type-safe data science frameworks. |
| 166 | + |
| 167 | +For the discussion, see [PR #7628](https://github.com/lampepfl/dotty/pull/7628). The documentation is available [here](https://dotty.epfl.ch/docs/reference/metaprogramming/inline.html#the-scalacompiletimeops-package). |
| 168 | + |
| 169 | +# Suggestions on missing contextual parameters |
| 170 | +If there's a compile-time error due to a missing contextual parameter and this error can be fixed with an import, the compiler will attempt to suggest such an import in the error message. Here is an example of how this error looks like: |
| 171 | + |
| 172 | +```scala |
| 173 | +-- Error: tests/neg/missing-implicit1.scala:17:4 ----------------------------------------------------------------------- |
| 174 | +17 | ff // error |
| 175 | + | ^ |
| 176 | + |no implicit argument of type testObjectInstance.Zip[Option] was found for parameter xs of method ff in object testObjectInstance |
| 177 | + | |
| 178 | + |The following import might fix the problem: |
| 179 | + | |
| 180 | + | import testObjectInstance.instances.zipOption |
| 181 | +``` |
| 182 | + |
| 183 | +One area where these suggestions will make life easier is purely functional programming with type-classes, with libraries like [cats](https://typelevel.org/cats/). Having the fix for a missing type class in the error message itself is a big time-saver. |
| 184 | + |
| 185 | +For the discussion, see [PR #7862](https://github.com/lampepfl/dotty/pull/7862). |
| 186 | + |
| 187 | +# Let us know what you think! |
| 188 | + |
| 189 | +If you have questions or any sort of feedback, feel free to send us a message on our |
| 190 | +[Gitter channel](https://gitter.im/lampepfl/dotty). If you encounter a bug, please |
| 191 | +[open an issue on GitHub](https://github.com/lampepfl/dotty/issues/new). |
| 192 | + |
| 193 | +## Contributing |
| 194 | + |
| 195 | +Thank you to all the contributors who made this release possible! |
| 196 | + |
| 197 | +According to `git shortlog -sn --no-merges 0.21.0-RC1..0.22.0-RC1` these are: |
| 198 | + |
| 199 | +``` |
| 200 | +TODO |
| 201 | +``` |
| 202 | + |
| 203 | +If you want to get your hands dirty and contribute to Dotty, now is a good time to get involved! |
| 204 | +Head to our [Getting Started page for new contributors](https://dotty.epfl.ch/docs/contributing/getting-started.html), |
| 205 | +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). |
| 206 | +They make perfect entry points into hacking on the compiler. |
| 207 | + |
| 208 | +We are looking forward to having you join the team of contributors. |
| 209 | + |
| 210 | +## Library authors: Join our community build |
| 211 | + |
| 212 | +Dotty now has a set of widely-used community libraries that are built against every nightly Dotty |
| 213 | +snapshot. Currently, this includes ScalaPB, algebra, scalatest, scopt and squants. |
| 214 | +Join our [community build](https://github.com/lampepfl/dotty-community-build) |
| 215 | +to make sure that our regression suite includes your library. |
| 216 | + |
| 217 | +[Scastie]: https://scastie.scala-lang.org/?target=dotty |
| 218 | + |
| 219 | +[@odersky]: https://github.com/odersky |
| 220 | +[@DarkDimius]: https://github.com/DarkDimius |
| 221 | +[@smarter]: https://github.com/smarter |
| 222 | +[@felixmulder]: https://github.com/felixmulder |
| 223 | +[@nicolasstucki]: https://github.com/nicolasstucki |
| 224 | +[@liufengyun]: https://github.com/liufengyun |
| 225 | +[@OlivierBlanvillain]: https://github.com/OlivierBlanvillain |
| 226 | +[@biboudis]: https://github.com/biboudis |
| 227 | +[@allanrenucci]: https://github.com/allanrenucci |
| 228 | +[@Blaisorblade]: https://github.com/Blaisorblade |
| 229 | +[@Duhemm]: https://github.com/Duhemm |
| 230 | +[@AleksanderBG]: https://github.com/AleksanderBG |
| 231 | +[@milessabin]: https://github.com/milessabin |
| 232 | +[@anatoliykmetyuk]: https://github.com/anatoliykmetyuk |
0 commit comments