Overall, this looks like a solid and comprehensive set of improvements to the language, elevating language past features like implicits from the level of mechanics to the level of intent, and simplifying teaching use case-driven contextual features (notably: type classes and extension methods).
My only very minor concerns revolve around reducing the number of ways to do things, and simplifying the new language features as much as possible.
Some random ideas here:
Remove Named Givens
Currently, there are two ways to define a given
:
given Ord[Int]
and
given intOrd: Ord[Int]
This bifurcation makes the feature more complex and forces users to choose between two different styles; indeed, it also leaks the mechanical, implementation detail that given
s are implemented by synthesizing named values (or methods).
The named given
is not needed, because it can be reconstructed separately from other orthogonal language features:
given Ord[Int] { ... }
val intOrd = summon[Ord[Int]]
This factoring avoids the conflation of creation of a given
with the naming and storing of a reference to that given
, and also takes a more opinionated stance that the canonical way to define given
instances is without names, which, thanks to summon
and extension methods, are often useless and / or boilerplate.
Remove Given Extensions
As others have pointed out, there are many ways to define extension methods. The syntax for defining extension methods is straightforward and an obvious generalization of the syntax for def
methods
However, given instances for extension methods introduce quite a different syntax for doing the same thing:
given stringOps: extension (xs: Seq[String]) { ... }
This new keyword and new language feature is unnecessary, because we can express the same extensions in two other ways: by defining method extensions using the new syntax, or by using given
instances on AnyRef
(or, indeed, by using implicit conversions!):
given AnyRef { ... }
The only additional cost for either technique is repeating the “this” parameter for every extension; but sometimes this is desirable, because for polymorphic data types, some extension methods are specialized (e.g. .flatten
defined only on Future[Future[A]]
); in general extension methods on polymorphic data types will not have a uniform target type.
Remove Implicit Conversions
This suggestion will be so flamed I won’t spend too time much on it, but the idea is that explicit toX
or asX
conversion methods can be added as extension methods, so implicit conversions are not actually fundamental or necessary in any way; and in my experience, the explicitness of extension method conversions helps tremendously with readability and tooling (e.g. toJavaCollection
instead of an automatic conversion).
Removing implicit conversions would also reduce the number of ways to do extension methods.
Other Random Notes
- I’d prefer the name
Equal
instead ofEql
- Big on given imports