【发布时间】:2016-05-20 03:41:08
【问题描述】:
我目前编写了一些实现类型类的类。我为此目的使用猫。在此设置中,我有课程 A[T] 和 B[T] <: A[T]。 A[T] 是一个 monad,所以在导入正确的库时,我可以写
val x: A[Int] = ???
x.flatMap(_)
但我不会写
val x: B[Int] = ???
b.flatMap(_)
因为 scala 没有找到 flatMap 运算符。 相反,我必须写
val x: B[Int] = ???
(b: A[Int]).flatMap(_)
为此工作。这很尴尬,因为我希望子类继承超类的方法。
为了完整起见,我添加了一个没有依赖关系的最小示例来显示问题。其中Filterable 是一个类型类,由S 实现。
object StackOverflowTest {
class S[+T]
class Q[+T] extends S[T]
trait Filterable[F[+_]] {
def filter[A](x: F[A]): F[A]
}
implicit class FilterOps[F[+_], A](y: F[A])(implicit ev: Filterable[F]) {
def filter: F[A] = ev.filter(y)
}
implicit object SIsFilterable extends Filterable[S] {
def filter[A](x: S[A]): S[A] = x
}
//def failing = {
// val q = new Q[Int]()
// val r = q.filter
//}
def working = {
val q = new Q[Int]()
val r = (q: S[Int]).filter
}
}
failing 定义无法编译,因为未找到 q.op。另一方面,working 通过首先转换为 S 来缓解此问题。我该怎么做才能使failing 示例适用于我图书馆的用户?您必须强制转换为父类型才能使用它的方法,这感觉非常不自然。
请注意,我无法定义第二个实现 Filterable[Q] 的隐式对象,因为 (new Q[Int]()).filter 的类型应该是 S[Int] 而不是 Q[Int]。
【问题讨论】:
-
类似的问题是
SomevsOption。这就是为什么猫为Xor、Validated、...提供智能构造函数的原因,因此您不会以Validated.Valid[Int]结尾,而是以Validated[Nothing, Int]结尾。也许可以使用Q遵循类似的策略? -
@PeterNeyens:不幸的是,这种策略是不可能的,因为 Q 有一些我想向用户公开的附加功能
标签: scala scala-cats