The most elegant way to model (at least some of these cases) without multi-level enums that I have found so far is using union types:
-
SizeInfo
:
type SizeInfo = Atomic | Bounded
enum Atomic {
case Infinite
case Precise(n: Int)
}
case class Bounded(bound: Int)
- Generalization of
Either
to include “Inclusive OR”
type AndOr[+A, +B] = Both[A, B] | Either[A, B]
case class Both[+A, +B](left: A, right: B)
enum Either[+A, +B]:
case Left[+A, +B](value: A) extends Either[A, B]
case Right[+A, +B](value: B) extends Either[A, B]
- JSON:
type JsValue = Obj | Arr | Primitive
case class Obj(fields: Map[String, JsValue])
case class Arr(elems: Array[JsValue])
type Primitive = Str | Num | JsNull.type | Bool
case class Str(str: String)
case class Num(bigDecimal: BigDecimal)
case object JsNull
enum Bool(boolean: Boolean):
case True extends Bool(true)
case False extends Bool(false)
The second example from the original post (the Hierarchy of Reals/Rationals/Integers/Naturals) is more tricky using Union Types as the top-level type has a shared member.