As suggested by @lihaoyi, I have broken out my comment on the principles of the new implicit design into a separate thread. Discussions about these principles or proposals of different design approaches would fit most naturally into this new thread.
The material here complements the Overview and Relationship with Scala-2 Implicits document pages with more thoughts on motivations and design principles. Because of its origin as a comment, it is written differently from other proposals in that it reflects my personal thoughts much more subjectively. So you should take it as an argument I bring forward rather than a neutrally formulated end result of deliberations.
I started the implicit redesign after having asked myself a hard question:
- If implicits are so good why are they not the run-away success they should be? Why do the great majority of people who are exposed to implicits hate them, yet the same people would love Haskell’s type classes, or Rust’s traits, or Swift’s protocols? The usual answer I get from people who are used to current implicits is that we just need minor tweaks and everything will be fine. I don’t believe that anymore.
- Otherwise put: What can we learn from the other languages? The main distinguishing factor is that their term synthesis is separate from the rest of programming, and that they more or less hide what terms get generated. What terms are generated is an implementation detail, the user should not be too concerned about it.
- By contrast, Scala exposes implementation details completely, and just by adding
implicitwe get candidates for term inference. The advantage of that approach is that it is very lightweight. We only need one modifier and that’s it. The disadvantage is that it is too low-level. It forces the programmer to think in terms of mechanism instead of intent. It is very confusing to learners. It feels a bit like Forth instead of Pascal. Yes, both languages use a stack for parameter passing and Forth makes that explicit. Forth is in that sense the much simpler language. But Pascal is far easier to learn and harder to abuse. Since I believe that’s an apt analogy I also believe that fiddling with Forth (i.e. current implicits) will not solve the problem.
So that led to a new approach that evolved over time. Along the way many variants were tried and discarded. In the end, after lots of experimentation, I arrived at the following principles:
- Implicit parameters and arguments should use the same syntax
- That syntax should be very different from normal parameters and arguments.
EDIT: In fact it’s better not to think of them as parameters at all, but rather see them as constraints.
- The new syntax should be clear also to casual readers. No cryptic brackets or symbols are allowed.
- There should be a single form of implicit instance definition. That syntax must be able to express monomorphic as well parameterized and conditional definitions, named as well as anonymous instances, and stand-alone instances as well as aliases.
- The new syntax should not mirror the full range of choices of the other definitions in Scala, e.g. val vs def, lazy vs strict, concrete vs abstract. Instead one should construct these definitions in the normal world and then inject them separately into the implicit world.
- Imports of implicits should be clearly differentiated from normal imports.
- Implicit conversions should be derived from the rest, instead of having their own syntax.
I arrived at these principles through lots of experimentation. Most of them were not present from the start but were discovered along the way. I believe these principles are worth keeping up, so I am pretty stubborn when it comes to weaken them. And I also believe that, given the mindshift these principles imply, there is no particular value to keep the syntax close to what it is now. In fact, keeping the syntax close has disadvantages for learning and migration.
It would be good to have people’s feedback on the principles themselves as well as on how the actual proposal fits with those principles.