Почему scala @tailrec нельзя использовать на Option.flatMap? - PullRequest
0 голосов
/ 22 февраля 2019

В scala следующие 2 функции служат одной и той же цели:

@tailrec
final def fn(str: String): Option[String] = {
  Option(str).filter(_.nonEmpty).flatMap { v =>
    fn(v.drop(1))
  }
}

@tailrec
final def fn2(str: String): Option[String] = {
  Option(str).filter(_.nonEmpty) match {
    case None    => None
    case Some(v) => fn2(v.drop(1))
  }
}

Однако @tailrec работает только во втором случае, в первом случае выдает следующую ошибку:

Ошибка: не удалось оптимизировать аннотированный метод @tailrec fn: он содержит рекурсивный вызов не в хвостовой позиции Option (str) .filter (_. NonEmpty) .flatMap {v =>

Почему эта ошибка была дана?И почему эти 2 кода генерируют байт-код JVM разных видов

Ответы [ 3 ]

0 голосов
/ 23 февраля 2019

Чтобы fn был хвостово-рекурсивным, рекурсивный вызов должен быть последним действием в функции.Если вы передадите fn другой функции, такой как flatMap, другая функция сможет выполнять другие действия после вызова fn, и поэтому компилятор не может быть уверен, что она является хвостовой рекурсивной.

В некоторыхВ этом случае компилятор может обнаружить, что вызов fn является последним действием в другой функции, но не в общем случае.И это зависит от конкретной реализации этой другой функции, поэтому аннотация tailrec может стать недействительной, если эта другая функция была изменена, что является нежелательной зависимостью.

0 голосов
/ 23 февраля 2019

Специально для последнего вопроса:

И почему эти 2 кода генерируют байт-код JVM разных типов

Поскольку в JVM нет гарантии, что JAR, содержащий Optionкласс во время выполнения такой же, как и во время компиляции.Это хорошо, потому что в противном случае даже второстепенные версии библиотек (включая стандартные библиотеки Java и Scala) были бы несовместимы, и вам нужно было бы использовать все зависимости, чтобы использовать одну и ту же младшую версию их общих зависимостей.

Если этоВ классе нет подходящего метода flatMap, вы получите AbstractMethodError, но в противном случае семантика Scala требует вызова метода flatMap.Таким образом, компилятор должен испускать байт-код для фактического вызова метода.

Kotlin обходит это, используя inline функции , и Scala 3 будет поддерживать их тоже , ноЯ не знаю, будет ли он использовать их для таких случаев.

0 голосов
/ 22 февраля 2019

Рассмотрим следующее:

List('a', 'b').flatMap(List(_,'g'))  //res0: List[Char] = List(a, g, b, g)

Мне кажется довольно очевидным, что flatMap() выполняет некоторую внутреннюю постобработку для достижения этого результата.Как еще можно объединить List('a','g') с List('b','g')?

...