如何在Scala中创建Int的子类型?

如何在Scala中创建Int的子类型?,scala,types,functional-programming,Scala,Types,Functional Programming,如何创建本质上是int的多个类型,即可以获得int值,可以使用+之类的数学运算符,但不同类型的实例不能混合 例如: val density1=新密度(100) val density2=新密度(200) 密度1+密度2应为(新密度(300)) val可变性=新的可变性(1) 可变性。数值应为(1) 密度1+可变性//不编译 这些类型可能有数百种,我不想在每个叶类中都实现像+这样的操作符 理想情况下,我希望避免所有隐式转换机制(仅限于个人偏好)。其他类型不应要求更改现有类型 trait Tagg

如何创建本质上是int的多个类型,即可以获得int值,可以使用+之类的数学运算符,但不同类型的实例不能混合

例如:

val density1=新密度(100)
val density2=新密度(200)
密度1+密度2应为(新密度(300))
val可变性=新的可变性(1)
可变性。数值应为(1)
密度1+可变性//不编译
这些类型可能有数百种,我不想在每个叶类中都实现像+这样的操作符

理想情况下,我希望避免所有隐式转换机制(仅限于个人偏好)。其他类型不应要求更改现有类型

trait TaggedInt[T <: TaggedInt[T]] {
  val value: Int
  protected def apply(value: Int): T
  def +(other: T) = apply(value + other.value)
  // etc.
}

case class Density(value: Int) extends TaggedInt[Density] {
  override protected def apply(value: Int) = Density(value)
}
我本来有

case class TaggedInt[Tag](value: Int) extends AnyVal {
  def +(other: TaggedInt[Tag]) = TaggedInt[Tag](value + other.value)
  // etc.
}

trait DensityTag
type Density = TaggedInt[DensityTag]

trait VariabilityTag
type Variability = TaggedInt[VariabilityTag]
但是对于这个用例,它至少有两个问题:

  • 密度(100)。toString
    标记力(100)
    而不是
    密度(100)
  • 密度(100)
    等于
    可变性(100)

  • 我认为Scala 3中有一个解决方案没有使用装箱/拆箱:

    对象包装器:
    不透明类型包装器=Int
    
    扩展[T为什么不只是在Int上创建一个包装器呢?Scala 3在这个用例中得到了支持。该页面描述了Scala 2的备选方案。@据我所知,问题是如何在
    Int
    上创建一个包装器,因为“这些类型可能有数百种,我不想在每个叶类中都实现+之类的运算符”我确实尝试过创建一个包装器。问题是如何混合数学运算符。可能有一种方法可以按特征提取运算符,例如Addable,但在未知类型的特征中构造新实例会有问题。@KarlBielefeldt不透明类型看起来像可伸缩的解决方案,我可以转到Scala 3。我想到了使用相同的东西,但我拼命地试图避免重复
    覆盖受保护的def apply(值:Int)=…
    所有time@slouc请参阅编辑。这与我目前拥有的几乎一字不差。感谢您指出这些限制。最初,它看起来像是通过从AnyVal继承来包装int的简单问题,但这会阻止进一步的继承,老实说,这看起来相当麻烦。这看起来很有趣,但不希望这样做使用反射实现性能和Liskov遵从性。奥德斯基说,你可以通过模式匹配“恢复类型信息”,但不清楚如何针对新类型关闭这些匹配语句。我认为不透明类型看起来像是解决此问题的可伸缩方式。有没有办法避免instanceof?@DuncanGreen
    。asInstanceOf[T]
    在这种情况下没有性能损失。由于它是在一个模块中封装的,所以它也非常安全。因此,外部用户不需要自己调用
    实例。@DuncanGreen这里没有反射,并且添加了
    可匹配的
    特性,您无法对不透明类型进行模式匹配,并发现它们都是可匹配的ally
    Int
    s。因此,我认为这是一种相当安全的方法。我认为您的解决方案可能比较安全,因为我不知道(t+other)如何解析为t以外的任何东西。一般来说,从基类型到更具体的类型的“向下转换”违反了Liskov,但我认为您在这种情况下不会这样做。太好了!
    case class TaggedInt[Tag](value: Int) extends AnyVal {
      def +(other: TaggedInt[Tag]) = TaggedInt[Tag](value + other.value)
      // etc.
    }
    
    trait DensityTag
    type Density = TaggedInt[DensityTag]
    
    trait VariabilityTag
    type Variability = TaggedInt[VariabilityTag]
    
    val density1 = Density(1)
    val density2 = Density(2)
    val density3: Density = density1 + density2    //compiles
    
    val check1: Variability = density1 + density2  //doesn't compile
    
    val variability = Variability(1)
    val check2 = (variability: Wrapper) + density2 //doesn't compile
    val check3 = variability + density2            //doesn't compile
    
    println(density1) //1
    println(density2) //2
    println(density3) //3