我想在Scala中了解有关 TypeClasses 的更多信息,并想出了这个示例
trait Cons[T] {
def cons(t1: T, t2: T):T
}
object Cons {
implicit object StringCons extends Cons[String] {
override def cons(t1: String, t2: String): String = t1 + t2
}
implicit object ListCons extends Cons[List[_]] {
override def cons(t1: List[_], t2: List[_]): List[_] = t1 ++ t2
}
implicit object IntCons extends Cons[Int] {
override def cons(t1: Int, t2: Int): Int = Integer.parseInt(t1.toString + t2.toString)
}
}
def Cons[T](t1: T, t2: T)(implicit c: Cons[T]):T = {
c.cons(t1, t2)
}
Cons("abc", "def") // abcdef
Cons(1, 2) // 12
Cons(List(1,2,3),List(4,5,6)) // does not work, as the expected type is List[Int]
当我创建ListCons
时,我明确设置了List[_]
的类型,如果我理解正确的是存在类型,则相当于Java通配符,这意味着它是某种类型,我们不在乎。
现在问题是为什么这不起作用。有没有办法让它发挥作用。或者也许我有一些根本的误解。
答案 0 :(得分:4)
Cons(List(1,2,3),List(4,5,6))
不起作用的原因是因为类型推断规则意味着Cons[List[Int]](List[Int](1,2,3), List[Int](4,5,6))
,所以它特别需要隐式Cons[List[Int]]
,而Cons[List[_]]
不是Cons[List[Int]]
。
Cons[List[_]]
将Cons[List[Int]]
如果Cons
是逆变的(声明为Cons[-T]
),但它不能是逆变的,因为它有一个方法返回T
(除非你欺骗@uncheckedVariance
,你不应该)。
使其正常工作的正确方法是将ListCons
替换为
implicit def listCons[T]: Cons[List[T]] = new Cons[List[T]] {
override def cons(t1: List[T], t2: List[T]): List[T] = t1 ++ t2
}
答案 1 :(得分:1)
我添加了更高级的kinded类型,可以捕获你的类型:
这是我的尝试:
trait Cons[T] {
def cons(t1: T, t2: T): T
}
trait HigherKindCons[M[_]] {
def cons[T](t1: M[T], t2: M[T]): M[T]
}
object Cons {
implicit object StringCons extends Cons[String] {
override def cons(t1: String, t2: String): String = t1 + t2
}
implicit object ListCons extends HigherKindCons[List] {
override def cons[T](t1: List[T], t2: List[T]): List[T] = t1 ++ t2
}
implicit object OptCons extends HigherKindCons[Option] {
override def cons[T](t1: Option[T], t2: Option[T]): Option[T] = t1 orElse t2
}
implicit object IntCons extends Cons[Int] {
override def cons(t1: Int, t2: Int): Int = Integer.parseInt(t1.toString + t2.toString)
}
}
import Cons._
def Cons[T](t1: T, t2: T)(implicit c: Cons[T]):T = {
c.cons(t1, t2)
}
def Cons[T, M[_]](t1: M[T], t2: M[T])(implicit c: HigherKindCons[M]): M[T] = {
c.cons(t1, t2)
}
Cons[String]("abc", "def") // abcdef
Cons(1, 2) // 12
Cons(List(1,2,3),List(4,5,6))
Cons(List("a", "b"),List("c", "d", "e")) // as the expected type is List[String]
Cons(Some(1), None)
res0: String = abcdef
res1: Int = 12
res2: List[Int] = List(1, 2, 3, 4, 5, 6)
res3: List[String] = List(a, b, c, d, e)
res4: Option[Int] = Some(1)