Класс типа Scala для расширения универсального типа: для параметра не обнаружено никаких последствий - PullRequest
0 голосов
/ 21 апреля 2019

Я хочу написать класс типов, чтобы добавить некоторое поведение к универсальному типу.Однако я не могу понять, как это сделать;Я продолжаю сталкиваться с ошибкой, приведенной ниже.

Представьте, что у вас есть универсальный тип MyList[A]:

trait MyList[A]

object MyList {
  case class Empty[A]() extends MyList[A]
  case class Node[A](value: A, next: MyList[A]) extends MyList[A]
}

Теперь вы хотите добавить некоторое поведение в этот класс, например преобразовать его вStream[A].Расширение на основе классов типов может показаться целесообразным:

// inspired by https://scalac.io/typeclasses-in-scala

trait MyListExt[T, A <: MyList[T]] {
  def stream(a: A): Stream[T]
}

object MyListExt {
  def apply[T, A <: MyList[T]](implicit a: MyListExt[T, A]): MyListExt[T, A] = a

  object ops {
    implicit class MyListExtOps[T, A <: MyList[T]](val a: A) extends AnyVal {
      def stream(implicit ext: MyListExt[T, A]): Stream[T] = ext.stream(a)
    }
  }

  private type T0

  implicit val myListToStreamImpl: MyListExt[T0, MyList[T0]] = new MyListExt[T0, MyList[T0]] {
    override def stream(a: MyList[T0]): Stream[T0] = {
      def fold[T1](l: MyList[T1], acc: Stream[T1]): Stream[T1] = l match {
        case MyList.Empty() => acc
        case MyList.Node(value, next) => fold(next, acc :+ value)
      }
      fold(a, Stream.empty)
    }
  }
}

Когда я сейчас пытаюсь использовать этот класс типов в своем коде, я получаю следующую ошибку в l.stream:

No implicits found for parameter ext: MyListExt[T_, MyList[Int]]
object MyListTest {
  def main(args: Array[String]): Unit = {
    import MyListExt.ops._
    val l: MyList[Int] = MyList.Node(1, MyList.Node(2, MyList.Node(3, MyList.Empty())))
    l.stream.foreach(println)
  }
}

Что я делаю не так или как мне заставить мой l.stream работать?
Я видел много примеров, включающих классы типов и неявное преобразование, но до сих пор ни один из них не работал с универсальными типами..

Ответы [ 2 ]

1 голос
/ 21 апреля 2019

Проблема в том, что в l.stream.foreach(println) l неявно преобразуется в new MyListExt.ops.MyListExtOps[.., ..](l), а дженерики выводятся как [Nothing, MyList[Int]], что не удовлетворяет [T, A <: MyList[T]].

Я не вижу причин для параметризации MyListExt с T и A <: MyList[T]. Я думаю, T достаточно, используйте MyList[T] вместо A.

Не используйте private type T0, просто параметризуйте myListToStreamImpl (сделайте это def) с помощью T0 aka T.

Попробуйте

  trait MyList[A]

  object MyList {
    case class Empty[A]() extends MyList[A]
    case class Node[A](value: A, next: MyList[A]) extends MyList[A]
  }

  trait MyListExt[T] {
    def stream(a: MyList[T]): Stream[T]
  }

  object MyListExt {
    def apply[T](implicit a: MyListExt[T]): MyListExt[T] = a

    object ops {
      implicit class MyListExtOps[T](val a: MyList[T]) extends AnyVal {
        def stream(implicit ext: MyListExt[T]): Stream[T] = ext.stream(a)
      }
    }

    implicit def myListToStreamImpl[T]: MyListExt[T] = new MyListExt[T] {
      override def stream(a: MyList[T]): Stream[T] = {
        def fold[T1](l: MyList[T1], acc: Stream[T1]): Stream[T1] = l match {
          case MyList.Empty() => acc
          case MyList.Node(value, next) => fold(next, acc :+ value)
        }
        fold(a, Stream.empty)
      }
    }
  }

  object MyListTest {
    def main(args: Array[String]): Unit = {
      import MyListExt.ops._
      val l: MyList[Int] = MyList.Node(1, MyList.Node(2, MyList.Node(3, MyList.Empty())))
      l.stream.foreach(println)
    }
  }
1 голос
/ 21 апреля 2019
  implicit def myListToStreamImpl[T]: MyListExt[T, MyList[T]] = new MyListExt[T, MyList[T]] {
    override def stream(a: MyList[T]): Stream[T] = {
      def fold(l: MyList[T1], acc: Stream[T1]): Stream[T1] = l match {
        case MyList.Empty() => acc
        case MyList.Node(value, next) => fold(next, acc :+ value)
      }
      fold(a, Stream.empty[T1])
    }
  }

Ваши типы не совпадают, потому что вы использовали это private type по любой причудливой причине. Типы, вложенные в объекты, имеют совершенно другое применение и не относятся к вашему текущему варианту использования.

...