O equivalente 'let' de Clojure em Scala

Muitas vezes, enfrento a seguinte situação: suponha que tenho essas três funções

def firstFn: Int = ...
def secondFn(b: Int): Long = ...
def thirdFn(x: Int, y: Long, z: Long): Long = ...

e eu também tenhocalculate função. Minha primeira abordagem pode ser assim:

def calculate(a: Long) = thirdFn(firstFn, secondFn(firstFn), secondFn(firstFn) + a)

Parece bonito e sem colchetes - apenas uma expressão. Mas como não é o ideal, acabo com este código:

def calculate(a: Long) = {
  val first = firstFn
  val second = secondFn(first)

  thirdFn(first, second, second + a)
}

Agora são várias expressões cercadas por colchetes. Nesses momentos, invejo Clojure um pouco. Comdeixe funcionar Eu posso definir esta função em uma expressão.

Então, meu objetivo aqui é definircalculate função com uma expressão. Eu venho com 2 soluções.

1 - Comscalaz Posso defini-lo assim (existem melhores maneiras de fazer isso com o scalaz?):

  def calculate(a: Long) = 
    firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}

O que eu não gosto nessa solução é que ela está aninhada. O maisvals Eu tenho mais profundo esse aninhamento.

2 - Comfor compreensão eu posso conseguir algo semelhante:

  def calculate(a: Long) = 
    for (first <- Option(firstFn); second <- Option(secondFn(first))) yield thirdFn(first, second, second + a)

Por um lado, esta solução tem estrutura plana, assim comolet no Clojure, mas por outro lado, preciso agrupar os resultados das funções emOption e receberOption como resultado decalculate (é bom que eu esteja lidando com nulos, mas eu não ... e não quero).

Existem melhores maneiras de alcançar meu objetivo? Qual é o caminho idiomático para lidar com essas situações (talvez eu deva ficar comvals ... maslet maneira de fazê-lo parece tão elegante)?

Por outro lado, está conectado aTransparência referencial. Todas as três funções são referencialmente transparentes (no meu exemplofirstFn calcula alguma constante como Pi); portanto, teoricamente, elas podem ser substituídas pelos resultados do cálculo. Eu sei disso, mas o compilador não, por isso não pode otimizar minha primeira tentativa. E aqui está minha segunda pergunta:

De alguma maneira (posso estar com anotação), posso dar uma dica ao compilador, que minha função é referencialmente transparente, para que ele possa otimizar essa função para mim (coloque algum tipo de cache lá, por exemplo)?

Editar

Obrigado a todos pelas ótimas respostas! É simplesmente impossível selecionar uma melhor resposta (pode ser porque todas são boas), por isso aceitarei a resposta com mais votos positivos, acho que é justo o suficiente.

questionAnswers(6)

yourAnswerToTheQuestion