Часто я сталкиваюсь с следующей ситуацией: предположим, что у меня есть эти три функции
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 function я могу определить эту функцию в одном выражении.
Итак, моя цель - определить функцию calculate
с одним выражением. Я придумал 2 решения.
1 - С scalaz Я могу определить его как это (есть ли лучшие способы сделать это с помощью scalaz?):
def calculate(a: Long) =
firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}
Что мне не нравится в этом решении, так это то, что он вложен. Чем больше val
, тем глубже это гнездование.
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), поэтому теоретически их можно заменить результатами расчета. Я знаю это, но компилятор этого не делает, поэтому он не может оптимизировать мою первую попытку. И вот мой второй вопрос:
Могу ли я каким-либо образом (может быть, с аннотацией) дать подсказку компилятору, что моя функция является ссылочной прозрачностью, так что она может оптимизировать эту функцию для меня (например, класть туда какое-то кеширование)?
Изменить
Спасибо всем за отличные ответы! Невозможно выбрать один лучший ответ (может быть, потому, что все они так хороши), поэтому я буду принимать ответ с самыми лучшими голосами, я думаю, это справедливо.