代码之家  ›  专栏  ›  技术社区  ›  franklin sharjeel

将参数限制为Scala中定义的类型

  •  0
  • franklin sharjeel  · 技术社区  · 7 年前

    我希望能够将对象包装在Scala中的容器中,以支持我正在开发的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))
          }
      }
    }
    

    我不认为变形是正确的工具。据我所知,这个“元”是无形的。换句话说,shapeveless允许我们将作为对象的类型参数限制为对象的某个子集。在这里,我试图将原语的类型参数限制为某个原语子集。

    1 回复  |  直到 7 年前
        1
  •  0
  •   SergGr    7 年前

    我不确定我是否理解你的问题,尤其是不清楚 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

    这段代码可能不是您真正想要的,但是如果没有使用示例,很难猜测,而且这可能是朝着正确方向迈出的一步。