Методы Scala и параметры типов с более высоким родом - PullRequest
4 голосов
/ 21 мая 2019

Я пытаюсь определить метод в Scala, который принимает универсальный тип S[_] <: Seq[Double] и возвращает S [FixedLoad] (FixedLoad - конкретный тип).Но моя реализация дает мне ошибки, и я не могу понять, почему.Несмотря на то, что я много раз пытался понять параметрические типы и типы с более высоким родом, мои знания растут очень медленно.

Я пытаюсь достичь того, чтобы не потерять конкретный тип S (подтип последовательности).

Вот код:

import scala.collection.generic.CanBuildFrom

class FixedLoad(val id: Int, val positionInT: Int, val amplitude: Double) {
  override def toString: String = s"FixedLoad($id, $positionInT, $amplitude)"
}

object Load {

  implicit def toFixedLoads[S[_] <: Seq[Double]](l: S[Double])(implicit cbf: CanBuildFrom[Nothing, FixedLoad, S[FixedLoad]]): S[FixedLoad] = {
    l.map(_ => new FixedLoad(1, 1, 1)).to[S]
  }

  def main(args: Array[String]): Unit = {
    println(toFixedLoads(List(1.0, 2.0, 3.0)))
  }

}

и ошибки:

Error:(16, 13) inferred type arguments [List] do not conform to method toFixedLoads's type parameter bounds [S[_] <: Seq[Double]]
    println(toFixedLoads(List(1.0, 2.0, 3.0)))

Error:(16, 30) type mismatch;
 found   : List[Double]
 required: S[Double]
    println(toFixedLoads(List(1.0, 2.0, 3.0)))

1 Ответ

5 голосов
/ 21 мая 2019

Краткий ответ:

Изменить toFixedLoads[S[_] <: Seq[Double]] на toFixedLoads[S[A] <: Seq[A]]

Длинный ответ:

Когда вы говоритеS[_], это тип с более высоким родом .Или, другими словами, это конструктор типа .Это означает, что для получения окончательного правильного типа требуется тип.Вот несколько примеров:

  • List - принимает тип, например Int, для получения правильного типа List[Int]
  • Option - принимает тип, напримерInt, для получения правильного типа Option[Int]

и т. Д.

Конструкторы типов этого типа часто представлены как * -> *.Вы предоставляете один тип, и вы получаете тип обратно.Есть и другие виды;например, Map и Either требуется два типа для получения правильного типа (например, Map[Int, String] или Either[Error, Foo]), поэтому их тип * -> * -> *.Думайте об этом как карри конструктор типа;принимает тип и возвращает функцию, которая принимает тип, а затем вы получаете окончательный, правильный тип.Или, другими словами, требуется два типа для получения окончательного, правильного типа.У вас также может быть конструктор типа, которому нужен конструктор типа для построения правильного типа (например, Monad[F[_]]), в этом случае тип равен (* -> *) -> * (например, List -> Monad[List]).

Поэтому, когда вы говорите, что ваш метод ожидает параметр типа S[Double], и вы передаете List(1.0, 2.0, 3.0), компилятор выводит S как List и жалуется, что List[A] не является подтипомSeq[Double] для любого A.Ваша первая попытка исправить это может быть F[_] <: Seq[_], но это не может скомпилировать, потому что внутренние типы по-прежнему не выравниваются.Нам нужно «связать» их чем-то вроде F[A] <: Seq[A] for some A, которое можно записать просто как F[A] <: Seq[A].

Хороший вопрос может быть: «Могу я сказать S <: Seq[Double]?»Конечно, S представляет собой правильный тип, так что вы могли бы!Что-то вроде этого работает просто отлично:

def foo[S <: Seq[Double]](s: S) = println(s)
foo(List(1.0, 2.0)) // prints List(1.0, 2.0)

Но, конечно, у вашего S есть "дыра", потому что ваш параметр метода имеет тип S[Double], поэтому он не применим в вашем случае.

...