1

我有一个隐式类来为案例类添加某个函数,例如:

case class TestClass(name:String)

implicit class ChangeProduct[S <: Seq[T], T <: Product](seq: Option[Seq[T]]) {
    def convert(expr: T => T): Option[Seq[T]] = seq.map(_.map(expr))
}

val c = Option(List(TestClass("a"), TestClass("b")))
val r = c.convert(p => p.copy(name = p.name.toUpperCase()))
println(r)

我很高兴看到输出是

Some(List(TestClass(A), TestClass(B)))

但现在我尝试通过将隐式类的参数更改为seq:Option[S]

implicit class ChangeProduct[S <: Seq[T], T <: Product](seq: Option[S]) {
    def convert(expr: T => T): Option[S] = seq.map(_.map(expr))
}

val c = Option(List(TestClass("a"), TestClass("b")))
val r = c.convert(p => p.copy(name = p.name.toUpperCase()))
println(r)

不幸的是,我收到了错误消息:

Error:(37, 51) type mismatch;
   found   : Seq[T]
   required: S
        def convert(expr: T => T): Option[S] = seq.map(_.map(expr))

为了表达p.copy(name = p.name.toUpperCase()),它说

Type mismatch.
    Required: Nothing => Nothing
    Found : Nothing => Any

我认为这可能是类型擦除问题,但我不知道如何解决它。

4

3 回答 3

3

问题不是类型擦除,而是我们需要使用类型构造函数S[_]而不仅仅是S. 考虑Functor约束

S[_]: Functor

像这样

import cats._
import cats.implicits._

case class TestClass(name:String)

implicit class ChangeProduct[S[_]: Functor, T](s: Option[S[T]]) {
  def convert(expr: T => T): Option[S[T]] = s.map(_.map(expr))
}

val c = Option(List(TestClass("a"), TestClass("b")))
c.convert(p => p.copy(name = p.name.toUpperCase()))

哪个输出

res0: Option[List[TestClass]] = Some(List(TestClass(A), TestClass(B)))
于 2019-10-11T07:35:35.517 回答
1

有两个问题:

  1. 第一条错误消息的原因是maponS不必 return S。它通常会这样做,但不能保证,当然也不会在类型中编码。

  2. 原因Nothing是不幸的是 Scala 的类型推断在这种情况下无法处理推断TS一起。

IterableOps您可以通过使用as bound 来解决这两个问题(在 2.13 中) :

implicit class ChangeProduct[S[A] <: IterableOps[A, S, S[A]], T <: Product](seq: Option[S[T]]) {
    def convert(expr: T => T): Option[S[T]] = seq.map(_.map(expr))
}

Product边界在这里似乎没有用)。

于 2019-10-11T08:20:53.733 回答
0

好吧,我想通了。如果我确实想限制 的Sbe 子类型Seq,我应该给它一个显式参数而不是下划线。也因为我有T <: ProductU必须是协变的:

implicit class ChangeProduct[S[+U] <: Seq[U], T <: Product](seq: Option[S[T]]) {
    def convert(expr: T => T): Option[Seq[T]] = seq.map(_.map(expr))
}

val c = Option(List(TestClass("a"), TestClass("b")))
val r = c.convert(p => p.copy(name = p.name.toUpperCase()))
println(r)

谢谢@Mario Galic 和@Alexey Romanov

于 2019-10-17T04:00:38.253 回答