diff --git a/build.sbt b/build.sbt index 407edaf6..517eb9a4 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,8 @@ -val scala3 = "3.1.0" +// Using an experimental feature like "erased definitions" requires a snapshot or nightly build +// of Scala, which you'll need to build locally. Clone the Dotty repo, +// https://github.com/lampepfl/dotty, and run `sbt publishLocal`. Then change the following +// definition to match the current version printed. +val scala3 = "3.1.3-RC1-bin-SNAPSHOT" lazy val root = project .in(file(".")) .settings( diff --git a/project/build.properties b/project/build.properties index 10fd9eee..c8fcab54 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.5.5 +sbt.version=1.6.2 diff --git a/src/main/scala/progscala3/erased/ErasedSeq.scala b/src/main/scala/progscala3/erased/ErasedSeq.scala new file mode 100644 index 00000000..315e3e57 --- /dev/null +++ b/src/main/scala/progscala3/erased/ErasedSeq.scala @@ -0,0 +1,45 @@ +package progscala3.erased + +import scala.annotation.implicitNotFound +import scala.language.experimental.erasedDefinitions + +sealed trait Emptiness +final class Empty extends Emptiness +final class NotEmpty extends Emptiness + +@implicitNotFound("The seq must be empty") +class IsEmpty[-E <: Emptiness] +object IsEmpty: + erased given IsEmpty[Empty] = new IsEmpty[Empty] + +@implicitNotFound("The seq must be not empty") +class IsNotEmpty[-E <: Emptiness] +object IsNotEmpty: + erased given IsNotEmpty[NotEmpty] = new IsNotEmpty[NotEmpty] + +sealed trait ESeq[+H, T <: ESeq[H,?]]: + type E <: Emptiness + def hd: H + def tl: T + def head(using erased ne: IsNotEmpty[E]): H = hd + def tail(using erased ne: IsNotEmpty[E]): T = tl + def +:[H2 >: H](h2: H2): NotEmptySeq[H2, this.type] + +case class NotEmptySeq[+H, T <: ESeq[H,?]] private (hd: H, tl: T) extends ESeq[H, T]: + type E = NotEmpty + def +:[H2 >: H](h2: H2): NotEmptySeq[H2, this.type] = + NotEmptySeq(h2, this) + override def toString: String = s"$head +: ${tail.toString}" + +object NotEmptySeq: + def apply[H, T <: ESeq[H,?]](hd: H, tl: NotEmptySeq[H, T]): NotEmptySeq[H, NotEmptySeq[H, T]] = + new NotEmptySeq(hd, tl) + def apply[H](hd: H): NotEmptySeq[H, EmptySeq.type] = new NotEmptySeq(hd, EmptySeq) + +case object EmptySeq extends ESeq[Nothing, Nothing]: + type E = Empty + def hd: Nothing = ??? + def tl: Nothing = ??? + def +:[H](h: H): NotEmptySeq[H, this.type] = NotEmptySeq(h) + override def toString: String = "EmptySeq" + diff --git a/src/main/scala/progscala3/erased/ErasedSeqMain.scala b/src/main/scala/progscala3/erased/ErasedSeqMain.scala new file mode 100644 index 00000000..56a53672 --- /dev/null +++ b/src/main/scala/progscala3/erased/ErasedSeqMain.scala @@ -0,0 +1,65 @@ +package progscala3.erased + +import scala.language.experimental.erasedDefinitions + +@main def Main() = + // Use the +: methods to construct sequences. + val nes1 = "one" +: EmptySeq + val nes2 = "two" +: nes1 + val nes3 = "three" +: nes2 + + // println(s"EmptySeq.head = ${EmptySeq.head}") // COMPILATION ERROR + // println(s"EmptySeq.tail = ${EmptySeq.tail}") // COMPILATION ERROR + + println(s"nes1 = $nes1") + println(s"nes1.head = ${nes1.head}") + println(s"nes1.tail = ${nes1.tail}") + // println(s"nes1.tail.head = ${nes1.tail.head}") // COMPILATION ERROR + // println(s"nes1.tail.tail = ${nes1.tail.tail}") // COMPILATION ERROR + + println(s"nes2 = $nes2") + println(s"nes2.head = ${nes2.head}") + println(s"nes2.tail = ${nes2.tail}") + println(s"nes2.tail.head = ${nes2.tail.head}") + println(s"nes2.tail.tail = ${nes2.tail.tail}") + // println(s"nes2.tail.tail.head = ${nes2.tail.tail.head}") // COMPILATION ERROR + // println(s"nes2.tail.tail.tail = ${nes2.tail.tail.tail}") // COMPILATION ERROR + + println(s"nes3 = $nes3") + println(s"nes3.head = ${nes3.head}") + println(s"nes3.tail = ${nes3.tail}") + println(s"nes3.tail.head = ${nes3.tail.head}") + println(s"nes3.tail.tail = ${nes3.tail.tail}") + println(s"nes3.tail.tail.head = ${nes3.tail.tail.head}") + println(s"nes3.tail.tail.tail = ${nes3.tail.tail.tail}") + // println(s"nes3.tail.tail.tail.head = ${nes3.tail.tail.tail.head}") // COMPILATION ERROR + // println(s"nes3.tail.tail.tail.tail = ${nes3.tail.tail.tail.tail}") // COMPILATION ERROR + + // Use the NotEmptySeq.apply methods + val nes1b = NotEmptySeq("one") + val nes2b = NotEmptySeq("two", nes1b) + val nes3b = NotEmptySeq("three", nes2b) + + println(s"nes1b = $nes1b") + println(s"nes1b.head = ${nes1b.head}") + println(s"nes1b.tail = ${nes1b.tail}") + // println(s"nes1b.tail.head = ${nes1b.tail.head}") // COMPILATION ERROR + // println(s"nes1b.tail.tail = ${nes1b.tail.tail}") // COMPILATION ERROR + + println(s"nes2b = $nes2b") + println(s"nes2b.head = ${nes2b.head}") + println(s"nes2b.tail = ${nes2b.tail}") + println(s"nes2b.tail.head = ${nes2b.tail.head}") + println(s"nes2b.tail.tail = ${nes2b.tail.tail}") + // println(s"nes2b.tail.tail.head = ${nes2b.tail.tail.head}") // COMPILATION ERROR + // println(s"nes2b.tail.tail.tail = ${nes2b.tail.tail.tail}") // COMPILATION ERROR + + println(s"nes3b = $nes3b") + println(s"nes3b.head = ${nes3b.head}") + println(s"nes3b.tail = ${nes3b.tail}") + println(s"nes3b.tail.head = ${nes3b.tail.head}") + println(s"nes3b.tail.tail = ${nes3b.tail.tail}") + println(s"nes3b.tail.tail.head = ${nes3b.tail.tail.head}") + println(s"nes3b.tail.tail.tail = ${nes3b.tail.tail.tail}") + // println(s"nes3b.tail.tail.tail.head = ${nes3b.tail.tail.tail.head}") // COMPILATION ERROR + // println(s"nes3b.tail.tail.tail.tail = ${nes3b.tail.tail.tail.tail}") // COMPILATION ERROR