Сопоставление коллекций и возврат контейнера того же типа, что и входные данные - PullRequest
1 голос
/ 21 октября 2019

В целях обучения я пытаюсь определить класс-оболочку с именем DoubleMap, который предоставляет метод mapBoth. По сути, он принимает две функции f и g, где домен g является совмещенным доменом f. Состав этой функции ( gof ) должен быть затем сопоставлен с контейнером, завернутым в DoubleMap

Это мой текущий код:

implicit class DoubleMap
[A, B, C, F[X] <: List[X]] // just List for now
(xs: F[A])(implicit cbf: CanBuildFrom[F[A], C, F[C]]) {
  def mapBoth(f: A => B)(g: B => C): F[C] =
    xs.map(f andThen g).to[F]
}

Однако, когдаЯ хочу использовать метод, подобный следующему:

List(true, false, false).mapBoth(!_)(!_)

Я получаю зашифрованное сообщение об ошибке о несоответствии типов между (найденным) типом CanBuildFrom[List[_], Nothing, List[Nothing]] и (обязательным) типом CanBuildFrom[List[Boolean], C, List[C]]

Почему компилятор выводит первый тип?

Ответы [ 2 ]

4 голосов
/ 21 октября 2019

Ваш вызов расширяется до

DoubleMap(xs).mapBoth(!_)(!_)

, поэтому все параметры типа DoubleMap и cbf должны быть выведены до обработки вызова mapBoth. Чтобы исправить это, просто переместите эти параметры в mapBoth:

implicit class DoubleMap
[A, F[X] <: List[X]] // just List for now
(xs: F[A]) {
  def mapBoth[B, C](f: A => B)(g: B => C)(implicit cbf: CanBuildFrom[F[A], C, F[C]]): F[C] =
    xs.map(f andThen g).to[F]
}

Тогда B и C определяются из f и g, а cbf из F,A и C.

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

Если вы не хотите привязываться к List, рассмотрите Functor от Cats

implicit class DoubleMap[F[_]: Functor, A](xs: F[A]) {
  def mapBoth[B, C](f: A => B)(g: B => C): F[C] =
    xs.map(f andThen g)
}

или ванильный подход Scala 2.13 (вдохновленный реализацией Luis '2.12 )

implicit class DoubleMap[F[x] <: IterableOnce[x], A](xs: F[A]) {
  def mapBoth[B, C](f: A => B)(g: B => C)(implicit bf: BuildFrom[F[A], C, F[C]]): F[C] =
    bf.fromSpecific(xs)(xs.iterator.map(f andThen g))
}

, которые оба выводят

List(true, false, false).mapBoth(!_)(!_)
Vector(true, false, false).mapBoth(!_)(!_)

res1: List[Boolean] = List(true, false, false)
res2: Vector[Boolean] = Vector(true, false, false)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...