|
| 1 | +object Try1 { |
| 2 | + // type-level naturals |
| 3 | + sealed trait TNat |
| 4 | + sealed trait TZero extends TNat |
| 5 | + sealed trait TSucc[T] extends TNat |
| 6 | + |
| 7 | + //reflect TNat at the value level; given n: Nat[T], n and T represent the same number. |
| 8 | + //This is what Haskellers call "singleton types" and took from Omega. |
| 9 | + // The point is that we can use such a number both at compile-time and at runtime, |
| 10 | + // but of course the runtime representation is not erased. |
| 11 | + //And this constrains the type argument of `Nat` to be of type `TNat` — though you could add bounds for it. |
| 12 | + |
| 13 | + sealed trait Nat[+T] |
| 14 | + case class Zero() extends Nat[TZero] |
| 15 | + case class Succ[T](n: Nat[T]) extends Nat[TSucc[T]] |
| 16 | + |
| 17 | + //We can index Vect with the types of value-level Nat, but this is a bit overkill. Still, no warnings. |
| 18 | + sealed trait Vect[N <: Nat[_], +T] |
| 19 | + case class VN[T]() extends Vect[Zero, T] |
| 20 | + case class VC[T, N <: Nat[_]](x: T, xs: Vect[N, T]) extends Vect[Succ[N], T] |
| 21 | + |
| 22 | + object Test { |
| 23 | + def foo[N <: Nat[_], A, B](v1: Vect[N, A], v2: Vect[N, B]) = |
| 24 | + (v1, v2) match { |
| 25 | + case (VN(), VN()) => 1 |
| 26 | + case (VC(x, xs), VC(y, ys)) => 2 |
| 27 | + } |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +//Since we didn't need value-level numbers, let's drop them: |
| 32 | +object Try2 { |
| 33 | + sealed trait TNat |
| 34 | + sealed trait TZero extends TNat |
| 35 | + sealed trait TSucc[T] extends TNat |
| 36 | + |
| 37 | + sealed trait Vect[N <: TNat, +T] |
| 38 | + case class VN[T]() extends Vect[TZero, T] |
| 39 | + case class VC[T, N <: TNat](x: T, xs: Vect[N, T]) extends Vect[TSucc[N], T] |
| 40 | + |
| 41 | + object Test { |
| 42 | + def foo[N <: TNat, A, B](v1: Vect[N, A], v2: Vect[N, B]) = |
| 43 | + (v1, v2) match { |
| 44 | + case (VN(), VN()) => 1 |
| 45 | + case (VC(x, xs), VC(y, ys)) => 2 |
| 46 | + } |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +//Same as Try2, but now `Vect` is covariant in `N` so we get the warning you described. |
| 51 | +object Try3 { |
| 52 | + sealed trait TNat |
| 53 | + sealed trait TZero extends TNat |
| 54 | + sealed trait TSucc[T] extends TNat |
| 55 | + |
| 56 | + sealed trait Vect[+N <: TNat, +T] |
| 57 | + case class VN[T]() extends Vect[TZero, T] |
| 58 | + case class VC[T, N <: TNat](x: T, xs: Vect[N, T]) extends Vect[TSucc[N], T] |
| 59 | + |
| 60 | + object Test { |
| 61 | + def foo[N <: TNat, A, B](v1: Vect[N, A], v2: Vect[N, B]) = |
| 62 | + //Warnings expected here! |
| 63 | + (v1, v2) match { |
| 64 | + case (VN(), VN()) => 1 |
| 65 | + case (VC(x, xs), VC(y, ys)) => 2 |
| 66 | + } |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +//Same as Try3, but now `Vect` is invariant |
| 71 | +object Try4 { |
| 72 | + sealed trait TNat |
| 73 | + sealed trait TZero extends TNat |
| 74 | + sealed trait TSucc[T] extends TNat |
| 75 | + |
| 76 | + sealed trait Vect[N <: TNat, +T] |
| 77 | + case class VN[T]() extends Vect[TZero, T] |
| 78 | + case class VC[T, N <: TNat](x: T, xs: Vect[N, T]) extends Vect[TSucc[N], T] |
| 79 | + |
| 80 | + object Test { |
| 81 | + def foo[N <: TNat, A, B](v1: Vect[N, A], v2: Vect[N, B]) = |
| 82 | + //Warnings expected here! |
| 83 | + (v1, v2) match { |
| 84 | + case (VN(), VN()) => 1 |
| 85 | + case (VC(x, xs), VC(y, ys)) => 2 |
| 86 | + } |
| 87 | + } |
| 88 | +} |
0 commit comments