我希望能够将对象包装在Scala的容器中,以支持我正在开发的AST类型语言。即,所有容器都将具有相等性,而某些容器将具有更扩展的布尔比较器(>=
,>
,<
,<=
)。 AST将支持以下类型:
Int
,BigDecimal
,String
,LocalDate
,Some(Int)
,Some(BigDecimal)
,Some(String)
,Some(LocalDate)
和以上任何单数类型的Set
,即Set[Int]
,但不是Set[AnyVal]
。
我已经设置好了
sealed trait Containable
// This trait will contain those types that can be subject to
// boolean comparison. Such as Date, BigDecimal, Int
// How can I restrict the value to these types?
sealed trait ContainableComparable extends Containable
// How can I restrict this to only Int, BigDecimal types?
// This should probably contain some sort of val numeric: ???
// But because BigDecimal is from scala.math and is not a true primitive
// (doesn't inherit from AnyVal), not sure how to limit this to a
// numeric value
sealed trait ContainableNumeric extends ContainableComparable
final case class ContainableInt(int: Int) extends ContainableNumeric
final case class ContainableBigDecimal(bd: BigDecimal) extends ContainableNumeric
final case class ContainableString(str: String) extends Containable
final case class ContainableBoolean(bool: Boolean) extends Containable
final case class ContainableDate(date: LocalDate) extends Containable
sealed trait Container {
val value: Containable
}
case class ContainerBoolean(value: ContainableBoolean) extends Container
case class ContainerNumeric(value: ContainableNumeric) extends Container
case class ContainerDate(value: ContainableDate) extends Container
case class ContainerString(value: ContainableString) extends Container
sealed trait ContainerSet {
val values: Set[Containable]
}
object ContainerSet {
def apply[T <: Containable](set: Set[T]): ContainerSet[Containable] = {
set apply {
case s: Set[ContainableInt] => ContainerSet[ContainableInt](s.map(ContainerNumeric.apply))
case s: Set[ContainableBigDecimal] => ContainerSet[ContainableBigDecimal](s.map(ContainerNumeric.apply))
case s: Set[ContainableString] => ContainerSet[ContainableString](s.map(ContainerString.apply))
case s: Set[ContainableDate] => ContainerSet[ContainbleDate](s.map(ContainerDate.apply))
}
}
}
在这里,我不认为塑形是正确的工具。根据我的理解,“元”是无形的。换句话说,无形状允许我们将作为对象的类型参数限制为对象的特定子集。在这里,我试图将原语的类型参数限制为原语的某个子集。
答案 0 :(得分:0)
我不确定我是否理解您的问题,尤其是目前尚不清楚应如何使用ContainableNumeric
。我仍然认为implicit
参数可能是解决问题的方法。您想要做的就是为您允许的类创建一个密封的typeclass,并要求该泛型类型受该类型类的约束。这是它的外观简化示例:
@implicitNotFound("""Cannot find implicit value for Containable[${T}]. The type ${T} is not Containable.""")
sealed trait Containable[T] {
def wrap(value: T): Container[T]
}
sealed trait ContainableComparable[T] extends Containable[T] with Ordering[T]
sealed trait ContainableNumeric[T] extends ContainableComparable[T]
object Containable {
private class ContainableImpl[T] extends Containable[T] {
override def wrap(value: T): Container[T] = new Container[T](value)(this)
}
private class ContainableNumericImpl[T: Numeric] extends ContainableImpl[T] with ContainableNumeric[T] {
override def compare(x: T, y: T): Int = implicitly[Numeric[T]].compare(x, y)
}
private class ContainableComparableImpl[T <: Comparable[T]] extends ContainableImpl[T] with ContainableComparable[T] {
override def compare(x: T, y: T): Int = x.compareTo(y)
}
implicit val containableInt: ContainableNumeric[Int] = new ContainableNumericImpl[Int]
implicit val containableBd: ContainableNumeric[BigDecimal] = new ContainableNumericImpl[BigDecimal]
implicit val containableString: ContainableComparable[String] = new ContainableComparableImpl[String]
implicit val containableBool: Containable[Boolean] = new ContainableImpl[Boolean]
}
sealed case class Container[T: Containable](value: T)
sealed case class ContainerSet[T: Containable](value: Set[T])
sealed class ContainerSet2[T: Containable](value: Set[Container[T]])
object ContainerSet2 {
def apply[T: Containable](set: Set[T]): ContainerSet2[T] = {
new ContainerSet2(set.map(el => implicitly[Containable[T]].wrap(el)))
}
}
这是一个用法示例:
def test(): Unit = {
val ci = Container(1)
val cs = Container("abc")
// val cd = Container(1.0)// fails with a compilation error
val csi = ContainerSet(Set(1))
val cs2i = ContainerSet2(Set(1))
// val csd = ContainerSet(Set(1.0))// fails with a compilation error
// val cs2d = ContainerSet2(Set(1.0))// fails with a compilation error
}
由于您没有使用ContainerSet
的示例,因此我提供了两种不同的可能实现:一种将整个Set
包装起来,而另一种将每个元素包装在集合内,就像您的原始代码一样。
此代码可能不是您真正想要的,但是如果没有用法示例,很难猜测,这可能是朝着正确方向迈出的一步。