Подтвердить что ты не робот

FlatMap (func) против flatMap (func (_))

Я очень удивлен тем, что не смог найти существующий вопрос для этого. Почему это, учитывая:

val p: Int => Option[Int] = Some(_)
List(1, 2, 3).flatMap(p)

Я получаю:

<console>:14: error: type mismatch;
 found   : Int => Option[Int]
 required: Int => scala.collection.GenTraversableOnce[?]
       List(1, 2, 3).flatMap(p)

Но если я заменю последнюю строку на эту, она компилируется и работает как ожидалось:

List(1, 2, 3).flatMap(p(_))

Мое решение проблемы заключается в том, что в случае p(_) система вывода типов выбирается, чтобы определить тип лямбда, и по пути находит соответствующее неявное преобразование для Option[Int] (option2Iterable, я считаю). Только с p тип уже известен, и он неверен, поэтому никакое преобразование не выполняется (и нет преобразования для Function1, возвращающего Option в Function1, возвращающего GenTraversableOnce).

Правильно ли это рассуждение? И если да, то есть причина, почему я не должен сообщать об этом как об ошибке/проблеме?

EDIT: новый поворот: я видел p.apply, упомянутый в некотором (к сожалению) удаленном комментарии (хотя это касалось стиля кодирования). Удивительно, но он работает так же хорошо, как и p(_).

4b9b3361

Ответ 1

Когда вы вводите List(1, 2, 3).flatMap(p(_)) то, что делается за кулисами, это то, что функция p генерируется и завершается другой функцией, которая ее частично применяет, что означает, что все необходимые имплицитные преобразования, если они есть, также будут применяться внутри тела этого новая функция.

При вводе List(1, 2, 3).flatMap(p) никакого приложения-функции не происходит, и вы пытаетесь передать Int => Option[Int], который несовместим с сигнатурой Int => GenTraversableOnce[Int], и хотя область содержит неявное преобразование от Option[T] до Iterable[T], нет преобразования от Function1[Int, Option[Int]] до Function1[Int, Iterable[Int]].

Причина этого, вероятно, состоит в том, что функции произвольной арности имеют практически бесконечное количество вариаций из-за дженериков, а поскольку Function не имеют сверхпридача, для этого потребуется довольно много имплицитов для каждого типа функций.


Здесь построение, которое расширяет flatMap достаточно для достижения желаемого результата для p. Тем не менее, он делает уже неясную сигнатуру flatMap еще менее ясной (гораздо менее ясной). Я полагаю, нет никакого технического блока от реализации этого поведения, но сложность сигнатур является причиной того, что часто называется реклама библиотеки scala -collections.

import scala.collection.GenTraversableOnce
import scala.collection.generic.CanBuildFrom

implicit class ListEx[A](list: List[A]) {
  def flatMap2[B, M[_], That](f: A => M[B])
                             (implicit bf: CanBuildFrom[List[A], B, That],
                                       view: M[B] => GenTraversableOnce[B]): That =
    list.flatMap(f andThen view)
}

val p: Int => Option[Int] = Some(_)

List(1, 2, 3) flatMap2 p

Ответ 2

List(1, 2, 3).flatMap(p(_))

скомпилирован для:

List(1,2,3).flatMap(x => p(x))

И при возврате p(x) Option[Int] и flatMap требуется GenTraversableOnce[Int], поэтому применяется scala.Option.option2Iterable.

Опция не наследуется от GenTraversableOnce. Чтобы этот синтаксис работал:

List(1,2,3).flatMap(p)

вам нужно неявное преобразование от Int => Option[Int] до Int => GenTraversableOnce[Int], sth, как это:

import scala.collection.GenTraversableOnce

implicit def conv(c: Int => Option[Int]): Int => GenTraversableOnce[Int] = {
    a => Option.option2Iterable(c(a))
}
val p: Int => Option[Int] = Some(_)
List(1, 2, 3).flatMap(p)

Для меня это не ошибка, но я согласен, это тоже не интуитивно.