类型从隐式 class 中删除
type is erased from implicit class
我有一个隐式 class 为案例 class 添加某个函数,例如:
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]
:
来使隐式 class 更通用
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
我认为这可能是类型擦除问题,但我不知道如何解决。
问题不在于类型擦除,而是我们需要使用类型构造函数 S[_]
而不仅仅是 S
。考虑约束
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)))
有两个问题:
第一条错误消息的原因是 S
上的 map
不必 return S
。它通常会,但不能保证,当然也不会在类型中编码。
Nothing
的原因是不幸的是 Scala 的类型推断无法处理在这种情况下一起推断 T
和 S
。
您可以通过使用 IterableOps
作为绑定来解决这两个问题(在 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
界限在这里似乎没有用)。
好吧,我想通了。如果我确实想将 S
限制为 Seq
的子类型,我应该给它一个显式参数而不是下划线。而且因为我有 T <: Product
,U
必须是协变的:
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
我有一个隐式 class 为案例 class 添加某个函数,例如:
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
我认为这可能是类型擦除问题,但我不知道如何解决。
问题不在于类型擦除,而是我们需要使用类型构造函数 S[_]
而不仅仅是 S
。考虑
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)))
有两个问题:
第一条错误消息的原因是
S
上的map
不必 returnS
。它通常会,但不能保证,当然也不会在类型中编码。Nothing
的原因是不幸的是 Scala 的类型推断无法处理在这种情况下一起推断T
和S
。
您可以通过使用 IterableOps
作为绑定来解决这两个问题(在 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
界限在这里似乎没有用)。
好吧,我想通了。如果我确实想将 S
限制为 Seq
的子类型,我应该给它一个显式参数而不是下划线。而且因为我有 T <: Product
,U
必须是协变的:
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