Constraining type parameters on case classes and traits

本秂侑毒 提交于 2021-01-29 14:27:07

问题


I'm a bit confused about how to constrain type parameters so that they will only accept types that have implemented a particular typeclass. Here's some rather contrived sample code:

// 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)

I wrote this expecting it to raise an error on the final line val four = ..., but in fact it is giving me a novel error (beyond the one I expected) - could not find implicit parameter for evidence value of type MyFavouriteTypesOfNumber[Int].

If anyone could a) let me know if my original code is correct for what I'm trying to achieve, or b) throw some light on this unexpected error message, then I'd greatly appreciate it.


回答1:


Use val for the type class implementations.

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

Change PresentMyFavourite into an abstract class so that we can use context bounds.

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

Now the rest should work except for the last line, val four ..., which, as expected, won't compile.

It should be noted that you're going to have to implement every math operation that you want to use for each of your "FavoriteTypesOfNumber". At this point the compiler knows that the type is restricted to Int or Float but, since it could be either, it doesn't know how to add 2 of them together unless you provide the code.



来源:https://stackoverflow.com/questions/63682367/constraining-type-parameters-on-case-classes-and-traits

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!