Компоновка и методы andThen

Я следую учебному руководству Соответствие шаблонов и функциональная композиция в Scala compose и andThen. Вот такой пример:

scala> def addUmm(x: String) = x + " umm"
scala> def addAhem(x: String) = x + " ahem"

val ummThenAhem = addAhem(_).compose(addUmm(_))

Когда я пытаюсь его использовать, я получаю сообщение об ошибке:

<console>:7: error: missing parameter type for expanded function ((x$1) => addAhem(x$1).compose(((x$2) => addUmm(x$2))))
   val ummThenAhem = addAhem(_).compose(addUmm(_))
                             ^
<console>:7: error: missing parameter type for expanded function ((x$2) => addUmm(x$2))
   val ummThenAhem = addAhem(_).compose(addUmm(_))
                                               ^
<console>:7: error: type mismatch;
 found   : java.lang.String
 required: Int
     val ummThenAhem = addAhem(_).compose(addUmm(_))

Однако это работает:

val ummThenAhem = addAhem _ compose addUmm _

или даже

val ummThenAhem = addAhem _ compose addUmm

Что не так с кодом в учебнике? Разве последнее выражение не совпадает с первым без круглых скобок?

Ответ 1

addAhem - метод. Метод compose определяется функциями. addAhem _ преобразует addAhem из метода в функцию, поэтому compose можно вызвать на нем. compose ожидает функцию как аргумент. Вы даете ему метод addUmm, преобразовывая addUmm в функцию с addUmm _ (подчеркивание может быть опущено, потому что компилятор может автоматически преобразовать метод в функцию, когда он знает, что функция ожидается в любом случае). Итак, ваш код:

addAhem _ compose addUmm

совпадает с

(addAhem _).compose(addUmm)

но не

addAhem(_).compose(addUmm(_))

PS Я не смотрел ссылку, которую вы предоставили.

Ответ 2

Ну, это:

addUhum _

- это расширение. Он преобразует методы в функции. С другой стороны, это:

addUhum(_)

- анонимная функция. Фактически, это приложение частичной функции, поскольку этот параметр не применяется, а вся вещь преобразуется в функцию. Он расширяется до:

x => addUhum(x)

Точные правила для расширения немного сложно объяснить, но, в основном, функция "начнет" в самом внутреннем разделителе выражений. Исключение составляют приложения с частичной функцией, где "x" перемещается за пределы функции - если вместо параметра используется _.

В любом случае, так оно расширяется:

val ummThenAhem = x => addAhem(x).compose(y => addUmm(y))

Увы, тип inferencer не знает тип x или y. Если вы хотите, вы можете точно увидеть, что он попробовал, используя параметр -Ytyper-debug.

Ответ 3

Из compose документации:

Составляет два экземпляра Function1 в новом Function1, с этим функция применяется последним.

поэтому вы должны написать

scala> val ummThenAhem = (addAhem _).compose(addUmm _)
ummThenAhem: String => java.lang.String = <function1>

рассматривать addAhem и addUmm как частично применяемые функции (т.е. function1)

scala> addAhem _
res0: String => java.lang.String = <function1>

Ответ 4

Я считаю, что учебник был написан для более ранней версии Scala (возможно, 2.7.7 или ранее). С тех пор произошли некоторые изменения в компиляторе, а именно расширения для системы типов, которые теперь приводят к отказу типа inferencing:

addUhum(_).compose(addAhem(_))

Подъем к функции по-прежнему работает с этим синтаксисом, если вы просто пишете:

addUhum(_)