Вопрос:
Рассмотрим простой ADT T = A | B
, то есть
sealed trait T
case object A extends T
case object B extends T
и классы Inner
и Outer
с меткой в T
, такой, что Outer
содержит Inner
.
trait Inner {
val label: T
}
trait Outer {
val label: T
val i: Inner
}
Я бы хотел статически обеспечить, чтобы оба Inner
внутри Outer
имели одинаковую метку. Каков был бы идиоматический подход для введения такой гарантии в Scala?
Моя попытка:
Путем включения типов в качестве полей и соответствующих уточнений.
trait Inner {
type Label <: T
val label: Label
}
trait Outer { self =>
type Label <: T
val label: Label
val i: Inner {type Label = self.Label}
}
Это работает до такой степени, в которой дано Inner
объектов с A
и B
,
object IA extends Inner {
override type Label = A.type
override val label: A.type = A
}
object IB extends Inner {
override type Label = B.type
override val label: B.type = B
}
и Outer
с A
можно взять только IA
, но не IB
.
object OA extends Outer {
override type Label = A.type
override val label: A.type = A
override val i: Inner{type Label = A.type} = IA
}
Хотя есть как минимум два недостатка.
1) Эта логика не обеспечивается чертами Inner
и Outer
. Объект должен иметь конкретное значение метки A
или B
, но его тип вместо A.type
или B.type
может быть шире T
.
object I2A extends Inner {
override type Label = T
override val label: T = A
}
object O2B extends Outer {
override type Label = T
override val label: T = B
override val i: Inner{type Label = T} = I2A
}
2), поскольку метки должныбыть одноэлементными объектами было бы неплохо, если бы можно было вызвать значение из типа. то есть что-то вроде:
trait Inner {
type Label <: T
val label: ??? // summon the single value for the singleton of type Label
}
trait Outer { self =>
type Label <: T
val label: ??? // summon the single value for the singleton of type Label
val i: Inner {type Label = self.Label}
}
, но первый недостаток уже показывает, что это невозможно, поскольку Label
может быть шире, чем A.type
и B.type
.