Dotty has added a really fabulous way of declaring Algebraic Data Types (ADTs) in the form of the new enum
syntax
e.g.
enum MyEnum {
case A
case B(i: Int, j: Int)
}
However these ADT are limited in that they can only be completely flat, while in general it is actually quite useful oftentimes to nest ADTs, and the new enum syntax seems like a great opportunity to provide support for those! I would propose the following syntax
enum MyEnum {
case A
case B(i: Int)
case enum C {
case D(s: String)
case E
case enum F {
case G
}
case enum H {
case I, J, K
}
}
}
But then the question arises: What type should be inferred for nested enum values, such as MyEnum.A
, MyEnum.B.apply
, and MyEnum.C.D.apply(s: String)
?
I think a simple approach would be: Always infer the most specific enclosing enum. So each of the following expressions should have these types inferred
MyEnum.A // MyEnum
MyEnum.B(1) // MyEnum
MyEnum.C.D("") // MyEnum.C
MyEnum.C.E // MyEnum.C
MyEnum.C.F.G // MyEnum.C.F
MyEnum.C.H.I // MyEnum.C.H
MyEnum.C.H.J // MyEnum.C.H
MyEnum.C.H.K // MyEnum.C.H
Below I will include a collection of nice usecases of this feature, to demonstrate that it is not just for giggles:
- Paul Philips’
SizeInfo
talked about here
enum SizeInfo {
case Bounded(bound: Int)
case enum Atomic {
case Infinite
case Precise(n: Int)
}
}
- A Hierarchy of Reals/Rationals/Integers/Naturals
enum Real(computeDigits: Int => BigDecimal) {
case enum Rational(numerator: Int, denominator: Int)
extends Real(_ => BigDecimal(numerator) / denominator) {
case enum Integer(n: Int) extends Rational(n, 1) {
case Natural(n: Int) extends Integer(n)
case Negative(n: Int) extends Integer(n)
}
case Fraction(numerator: Int, denominator: Int) extends Rational(numerator, denominator)
}
case Irrational(computeDigits: Int => BigDecimal) extends Real(computeDigits)
}
- An generalization of
Either
to include “Inclusive OR”
enum AndOr[+A, +B] {
case Both[+A, +B](left: A, right: B) extends AndOr[A, B]
case enum Either[+A, +B] extends AndOr[A, B] {
case Left[+A, +B](value: A) extends Either[A, B]
case Right[+A, +B](value: B) extends Either[A, B]
}
}
- Grouping JSON values into primitives and non primitives
enum JsValue {
case Obj(fields: Map[String, JsValue])
case Arr(elems: ArraySeq[JsValue])
case enum Primitive {
case Str(str: String)
case Num(bigDecimal: BigDecimal)
case JsNull
case enum Bool(boolean: Boolean) {
case True extends Bool(true),
case False extends Bool(false) }
}
}