I had raised the suggestion for extensions to only use the “Collective Extension” syntax (to simplify the language). Martin had countered that the collective extension syntax isn’t sufficient to implement type classes.
I have read about type classes in Scala 2 in the past, and have some use cases where they would probably have been useful, but I shied away from them because they looked too complicated to define/understand. I wasn’t convinced that a regular engineer would be able to understand the code, and perhaps even I wouldn’t understand the code after coming back to it six months later.
I wasn’t aware of Simulacrum, but from a language user perspective this is fairly close to what I would like type classes to be. I.e. something that is fairly concisely defined in the language where the construct is easily identified.
Hence, without understanding the exact mechanics of how it works, and I appreciate that the devil is in the detail, I think that I was hoping that typeclasses in Scala 3 might look something more like this:
trait SemiGroup[T] {
@infix def combine (x: T)(y: T): T
}
typeclass trait Monoid[T] extends SemiGroup[T] {
def unit: T
}
typeclass instance Monoid[String] {
def combine (x: String)(y: String): String = x.concat(y)
def unit: String = ""
}
typeclass instance Monoid[Int] {
def combine (x: Int)(y: Int): Int = x + y
def unit: Int = 0
}
extension on (xs: List[T]) {
def sum[T: Monoid]: T = xs.foldLeft(Monoid[T].unit)(_ combine _)
}
---
trait Functor[F[_]] {
def map[A, B](x: F[A])(f: A => B): F[B]
}
typeclass trait Monad[F[_]] extends Functor[F] {
def flatMap[A, B](x: F[A => B])(f: F[A]): F[B]
def map[A, B](x: F[A])(f: A => B): F[B] = x.flatMap(f `andThen` pure)
def pure[A](x: A): F[A]
}
typeclass instance listMonad as Monad[List] {
def flatMap[A, B](xs: List[A])(f: A => List[B]): List[B] =
xs.flatMap(f)
def pure[A](x: A): List[A] =
List(x)
}
// ReaderMonad omitted, I didn't under the the =>> syntax.
Note, I’m not saying the the code above can work, but just to give an indication of what I was hoping to see. With the current proposed design, I think that I would still either avoid defining typeclasses, or add additional code comments to explain what is being done. Or perhaps wait for someone to port something like Simulacrum to Scala 3.
I think ultimately, I was hoping that “givens/implicits” are treated as low level language machinery that folks don’t generally need to understand or make use of, with cleaner high level abstractions sitting on top of them.
Parts of the Scala language are exceptionally nice to use, but it worries me that parts of the language (currently need to be used by library definitions/implementations) are complex enough that I can’t fully understand the code without investing considerable mental agility.