【问题标题】:Constraining type parameters on case classes and traits约束案例类和特征的类型参数
【发布时间】:2020-12-20 06:08:00
【问题描述】:

我有点困惑如何约束类型参数,以便它们只接受已实现特定类型类的类型。这是一些相当做作的示例代码:

// I want to tag my favourite types of number with this trait
trait MyFavouriteTypesOfNumber[A]
// implicits necessary because I cannot directly extend Int or Double
implicit def iLikeInts(i: Int): MyFavouriteTypesOfNumber[Int] = new MyFavouriteTypesOfNumber[Int] {}
implicit def iAlsoLikeFloats(f: Float): MyFavouriteTypesOfNumber[Float] = new MyFavouriteTypesOfNumber[Float] {}

// Is the type constraint here correct? 
// I am trying to say 'only accept types that implement MyFavouriteTypesOfNumber'
trait PresentMyFavourite[Num <: MyFavouriteTypesOfNumber[Num]] {
  val originalNum: Num
  def packageAsString: String = s"***... ${originalNum} ...***"
  def printPackaged: Unit = println(packageAsString)
}

// again, the case class should only accept types that have implemented MyFavouriteTypesOfNumber
case class ILikeThisNumberAsWellAsItsType[Num: MyFavouriteTypesOfNumber] (
  val originalNum: Num
) extends PresentMyFavourite[Num]

// Would expect these next two lines to work
val three = ILikeThisNumberAsWellAsItsType[Int](3: Int)
three.printPackaged
// But not this one, because I don't like Doubles
val four = ILikeThisNumberAsWellAsItsType[Double](3.0: Double)

我写这篇文章是希望它在最后一行 val four = ... 上引发错误,但实际上它给了我一个新错误(超出了我的预期) - could not find implicit parameter for evidence value of type MyFavouriteTypesOfNumber[Int]

如果有人可以 a) 让我知道我的原始代码对于我想要实现的目标是否正确,或者 b) 对这个意外错误消息有所了解,那么我将不胜感激。

【问题讨论】:

    标签: scala generics typeclass


    【解决方案1】:

    val 用于类型类实现。

    trait MyFavouriteTypesOfNumber[A]
    implicit val iLikeInts: MyFavouriteTypesOfNumber[Int] =
      new MyFavouriteTypesOfNumber[Int] {}
    implicit val iAlsoLikeFloats: MyFavouriteTypesOfNumber[Float] =
      new MyFavouriteTypesOfNumber[Float] {}
    

    PresentMyFavourite 更改为abstract class,以便我们可以使用上下文边界。

    abstract class PresentMyFavourite[Num : MyFavouriteTypesOfNumber] {
      val originalNum: Num
      def packageAsString: String = s"***... ${originalNum} ...***"
      def printPackaged(): Unit = println(packageAsString)
    }
    

    现在除了最后一行 val four ... 之外的其余部分应该可以工作了,正如预期的那样,它不会编译。

    应该注意的是,您将必须为每个“FavoriteTypesOfNumber”实现您想要使用的每个数学运算。此时编译器知道该类型被限制为IntFloat,但是,因为它可能是任何一个,所以除非您提供代码,否则它不知道如何将它们中的两个相加。

    【讨论】:

    • 谢谢。让我有点惊讶的是,约束一个案例类的泛型的语法(至少在这种情况下)与约束一个特征的泛型的语法不同。这是因为案例类总是使用具体类型吗?
    • 出于兴趣,是否可以使用 trait 实现这种通用约束?
    • &lt;:: 只是两种不同类型的约束(“上限”和“上下文绑定”),而不是用于约束特征和类的不同语法。 : 不能用于特征,仅因为它转换为构造函数参数,但 &lt;: 可以用于类。
    • @Chrisper;特征不接受值参数,因此您不能使用上下文绑定约束,因为它们是在隐式参数中翻译的。 Scala 3 将消除这种区别并允许使用 trait 参数。
    猜你喜欢
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 2018-04-11
    • 1970-01-01
    • 2014-10-02
    相关资源
    最近更新 更多