I agree with soronpo. Sealed types and match types are actually unifiable.
I think we should treat the code after the declaration as a normal statement:
type Foo = {
//This Tree must return a type
}
Match types
With this change, match types could be written like this:
type LeafElem[X] = X match { //Simple tree with pattern matching
case String => Char
case Array[T] => LeafElem[T]
case Iterable[T] => LeafElem[T]
case AnyVal => X
}
As you can see, the match type definition should not change if we treat type definition as a “normal” Tree.
Sealed Trait
For value-check, I think we could use a method-like syntax type Foo(original: Int) = {}
:
type Num(n: Int) = n match {
case 0 => Zero
case n if n > 0 => Pos
case _ => Neg
}
Note: here we don’t use singleton types because the above example should support non-litteral values.
We should only be able to pass one argument:
val t: Num = 5 //Here 5 is passed as the `n` argument in the above example.
But what’s the aliased/returned type ? I see two options:
- Make an union of all possibly returned types
- Force specifying the aliased type.
For example to make the compiler understand I can use int-related methods on my Num, I could define it like that:
type Num(n: Int) <: Int = n match {
case 0 => Zero
case n if n > 0 => Pos
case _ => Neg
}
Note: here I use <:
because it is already used for opaque types and because our type return a subtype of Int.
Opaque types
With this approach, opaque types are some kind of “inline types”. The definition Tree should be inlined:
opaque type Num(n: Int) <: Int = n match { //Inlined match
case 0 => Zero
case n if n > 0 => Pos
case _ => Neg
}
Note: Here n
must be inlined. Maybe we should specify inline n: Num
?
This approach allow us to use inline methods like quotes’ error/warning:
opaque type Positive(n: Int) = n match {
case n if n > 0 => Int
case _ => quotes.reflect.report.error(s"$n must be positive.")
}
Finally, using an inlined Tree allow us to use singleton types as value arguments:
import scala.compiletime.requireConst
opaque type Between(n: Int)[A <: Int, B <: Int](n: Int) <: Int = {
val a = requireConst[A]
val b = requireConst[B]
if(n > a && n < b) Int else quotes.reflect.report.error(s"$n must be between $a and $b")
}
val x: Between[0, 5] = 4
val y: Between[0, 5] = 6 //Compile time error