Часто я сталкиваюсь со следующей ситуацией: предположим, у меня есть эти три функции
def firstFn: Int = ...
def secondFn(b: Int): Long = ...
def thirdFn(x: Int, y: Long, z: Long): Long = ...
и у меня также есть функция calculate
. Мой первый подход может выглядеть так:
def calculate(a: Long) = thirdFn(firstFn, secondFn(firstFn), secondFn(firstFn) + a)
Выглядит красиво и без фигурных скобок - только одно выражение. Но это не оптимально, поэтому я получаю следующий код:
def calculate(a: Long) = {
val first = firstFn
val second = secondFn(first)
thirdFn(first, second, second + a)
}
Теперь это несколько выражений, заключенных в фигурные скобки. В такие моменты я немного завидую Clojure. С помощью let функции я могу определить эту функцию в одном выражении.
Итак, моя цель - определить функцию calculate
с одним выражением . Я придумываю 2 решения.
1 - С помощью scalaz Я могу определить это следующим образом (есть ли лучшие способы сделать это с помощью скалаза?):
def calculate(a: Long) =
firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}
Что мне не нравится в этом решении, так это то, что оно является вложенным. Чем больше val
s у меня есть, тем глубже это вложение.
2 - С пониманием for
я могу достичь чего-то похожего:
def calculate(a: Long) =
for (first <- Option(firstFn); second <- Option(secondFn(first))) yield thirdFn(first, second, second + a)
С одной стороны, это решение имеет плоскую структуру, как и let
в Clojure, но с другой стороны, мне нужно обернуть результаты функций в Option
и получить Option
как результат calculate
(это хорошо это я имею дело с нулями, но я не ... и не хочу).
Есть ли лучшие способы для достижения моей цели? Каков идиоматический способ решения таких ситуаций (может быть, я должен остаться с val
s ... но let
способ сделать это выглядит так элегантно)?
С другой стороны он подключен к Ссылочная прозрачность . Все три функции являются ссылочно прозрачными (в моем примере firstFn
вычисляет некоторую константу, например, Pi), поэтому теоретически их можно заменить результатами вычислений. Я знаю это, но компилятор не знает, поэтому он не может оптимизировать мою первую попытку. И вот мой второй вопрос:
Могу ли я как-то (может быть с аннотацией) дать подсказку компилятору, что моя функция прозрачна по ссылкам, чтобы она могла оптимизировать эту функцию для меня (например, поместить туда какое-то кэширование)? *
Редактировать
Спасибо всем за отличные ответы! Просто невозможно выбрать один лучший ответ (может быть, потому что все они так хороши), поэтому я приму ответ с наибольшим количеством голосов, я думаю, что это достаточно справедливо.