问题
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