тип стирается из неявного класса - PullRequest
1 голос
/ 11 октября 2019

У меня есть неявный класс для добавления определенной функции для класса case, например:

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

Я думаю, что это может быть проблема стирания типа, но я не знаю, как ее исправить.

Ответы [ 3 ]

3 голосов
/ 11 октября 2019

Проблема не в стирании типов, но нам нужно использовать конструктор типов 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)))
1 голос
/ 11 октября 2019

Есть две проблемы:

  1. Причиной первого сообщения об ошибке является то, что map на S не должно возвращать S. Обычно так и есть, но это не гарантировано и, конечно, не закодировано в типах.

  2. Причина Nothing в том, что, к сожалению, вывод типа Scala не может обрабатывать вывод T и S вместе в этой ситуации.

Вы можете исправить обе проблемы (в 2.13), используя вместо этого IterableOps в качестве границы:

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 не кажется полезнойВот).

0 голосов
/ 17 октября 2019

Хорошо, я понял это. Если я хочу ограничить подтип 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

...