问题描述
看看下面的代码:
case class MyTypeConstructor[T[_]: Seq,A](mySeq: T[A]) {
def map[B](f: A => B): T[B] = mySeq.map(f) // value map is not a member of type parameter T[A]
}
case class MyTypeBounds[T[A] <: Seq[A],A](mySeq: T[A]) {
def map[B](f: A => B): T[B] = mySeq.map(f)
}
理想情况下,两者都会做同样的事情,只需定义一个从 map
调用 map
方法的虚拟 Seq
。但是,第一个不会编译,而第二个有效(实际上第二个也不起作用,但为了简单起见,我省略了一些内容)。
我得到的编译错误是 T[A]
没有成员 map
,但我很奇怪,因为类型构造函数 T
应该返回一个 Seq
(它确实有map
)。
谁能解释一下这两种实现在概念上有何不同?
解决方法
T[_]: Seq
这并不是说“T[_]
应该返回一个 Seq
-like this”。这就是您的第二个示例正确说明的内容。这表示“T[_]
应该满足名称为 Seq
的隐式”。但是 T
需要参数,所以它不能真正成为隐式的一部分。本质上,它正在尝试做
case class MyTypeConstructor[T[_],A](mySeq: T[A])(implicit arg: Seq[T[_]])
但是 Seq[T[_]]
作为函数的参数没有意义,首先是因为 T
需要一个未提供的参数*,其次是因为 Seq
不打算成为用作隐式。
我们可以看到这是一个奇怪的构造,因为您可以删除 myMap
并仍然得到错误。
// error: type T takes type parameters
case class MyTypeConstructor[T[_]: Seq,A](mySeq: T[A]) {}
*从理论上讲,编译器可以将 T[_]: Seq
视为需要隐式存在参数的声明,但这不是它现在所做的,并且即使它确实有用也会有问题。
这两种实现在概念上有何不同?
我们可以使用子类型或类型类方法来约束多态类型参数
scala> case class Subtyping[T[A] <: Seq[A],A](xs: T[A]) {
| def map[B](f: A => B) = xs.map(f)
| }
|
| import scala.collection.BuildFrom
|
| case class TypeClassVanilla[T[x] <: IterableOnce[x],A](xs: T[A]) {
| def map[B](f: A => B)(implicit bf: BuildFrom[T[A],B,T[B]]): T[B] =
| bf.fromSpecific(xs)(xs.iterator.map(f))
| }
|
| import cats.Functor
| import cats.syntax.all._
|
| case class TypeClassCats[T[_]: Functor,A](xs: T[A]) {
| def map[B](f: A => B): T[B] =
| xs.map(f)
| }
class Subtyping
import scala.collection.BuildFrom
class TypeClassVanilla
import cats.Functor
import cats.syntax.all._
class TypeClassCats
scala> val xs = List(1,2,3)
val xs: List[Int] = List(1,3)
scala> Subtyping(xs).map(_ + 1)
val res0: Seq[Int] = List(2,3,4)
scala> TypeClassCats(xs).map(_ + 1)
val res1: List[Int] = List(2,4)
scala> TypeClassVanilla(xs).map(_ + 1)
val res2: List[Int] = List(2,4)
它们是实现同一目标的不同方法。使用类型类方法,也许我们不必担心组织继承层次结构,随着系统复杂性的增长,这可能会导致我们开始人为地将事物强制为层次结构。