I'm recently migrating some libs and projects to Scala 3, I guess it would be very helpful to me or anyone interested to learn some new functional programming features that Scala 3 is bringing to us.
- Rank N Types
- Phantom Types
- Dependent Types
- "First Class" Types
- Type Classes
- Generic Type Class Derivation
Source code 👉 https://github.com/jcouyang/meow
ADT is something that has multiple constructors return single type:
enum List[+A]: case Nil case Cons(head: A, tail: List[A])
Cons can create a value of type
This is great improvement from Scala 2, since defining an ADT is much easier than before:
sealed trait List[+A] case object Nil extends List[Nothing] case class Cons[A](head: A, tail: List[A]) extends List[A]
But actually the way Scala 2 using is GADT.
We can also do GADT using
, for instance to define a
1: enum Size: 2: case Empty 3: case NonEmpty 4: 5: enum SafeList[+A, +S <: Size]: 6: case Nil extends SafeList[Nothing, Size.Empty.type] // <- 7: case Cons(head: A, tail: SafeList[A, Size]) extends SafeList[A, Size.NonEmpty.type]
What GATD provides fine control of type, i.e. line 6 no longer returns
we can let it return something else
Same way we can make
SafeList[A, Size.NonEmpty.type], which tag it as
NonEmpty at type level.
So we can simply write a method
safeHead just handle
NonEmpty List, and it is safe at compile time.
import SafeList._ def safeHead[A](list: SafeList[A, Size.NonEmpty.type]): A = list match case SafeList.Cons(head, tail) => head
When a Nil is passed to
safeHead, compiler will point it out:
Found: (Main.SafeList.Nil : Main.SafeList[Nothing, (Main.Size.Empty : Main.Size)]) Required: Main.SafeList[Any, (Main.Size.NonEmpty : Main.Size)]
Try it online at Scastie: https://scastie.scala-lang.org/jcouyang/yGQTSUJ6SN2P2oUsfWu9zw/1
Or clone the repo and
sbt test: https://github.com/jcouyang/meow