The standard library’s lack of Monoid seems to be an important symptom
I found that Scala’s typeclass conventions contributed as much or more to my learning curve than the theory of typeclasses/semantics. I haven’t found any place that these conventions are explained well except “Scala with Cats”.
For example in the circe library we have Decoder.instance
, this used to seem arcane and exactly the kind of thing that might make newcomers feel stupid and detracted from continuing with Scala. After reading “Scala with Cats” I now see “Oh, Decoder.instance is a helper function to construct a typeclass instance for the Decoder typeclass, and this is a common convention across Scala”.
Compared to a language like golang, Scala might seem to suffer from “canonical Scala” requiring layers of conventions and non-standard libraries that are exogenous to the “official” language. It sounds like the mission for Scala 3 acknowledges some of this and is attempting to build some best practices into the language.
For example, if typeclasses are such a powerful, obvious, necessary part of Scala, why is there no Monoid in the standard library? It seems to me that by respecting the diversity of approaches to these core language features (eg. scalaz vs cats) we make Scala less approachable because newcomers must learn 1. the language, 2. the standard library, 3. the “standard” non-standard libraries (for which discovery is a huge issue for newcomers), and 4. the conventions to weave them all together. In golang a user need only learn (1) and (2) and I think this should be the case for Scala 3 as well.
Also, things I do with implicits that I hope are supported in Scala 3:
I’m writing a game in Scala where performance is a major issue and empirically dominated by dynamic memory allocations. To meet this goal I have found these practices related to implicits to be useful:
- avoiding use of
implicit def foo[A]
because it reallocates typeclass instances each interface call, instead doing manual specialization withval fooA = foo[A]
. (I know dotty has done work on instance derivation but I haven’t studied it) - impure “typeclass instances” eg.
DoubleLinkedList.setNext(node, nextNode) = node.concreteNextNode = nextNode
- “typeclass instances” that are both impure and close over context, eg. an instance of DoubleLinkedList which sets head of list by
def setHead(container, newHead) = this.myMap(this.contextualKey)(container) = newHead
-
def DoubleLinkedList.isEmpty[A, B, C, H](h: H, t: InferType[A])(implicits using A, B, C, H)
where the type parameters can’t be inferred by the compiler with the argh
, but can be inferred with a hint aboutA
, where InferType[A]: Option[A] = None, preventing the need for the client to writeisEmpty[verbose concrete type parameters](h)
These practices have helped me leverage implicits and I hope they are supported in the implicit redesign.