Por que o Scala exige que as funções tenham um tipo de retorno explícito?

11

Recentemente, comecei a aprender a programar em Scala, e tem sido divertido até agora. Eu realmente gosto da capacidade de declarar funções dentro de outra função que parece algo intuitivo a ser feito.

Uma irritação que tenho sobre Scala é o fato de Scala exigir um tipo de retorno explícito em suas funções . E eu sinto que isso atrapalha a expressividade da linguagem. Também é difícil programar com esse requisito. Talvez seja porque eu venho da zona de conforto Javascript e Ruby. Porém, para uma linguagem como Scala, que terá toneladas de funções conectadas em um aplicativo, não consigo imaginar como faço para pensar exatamente em que tipo a função específica que estou escrevendo deve retornar com recursões após recursões.

Esse requisito de declaração explícita do tipo de retorno em funções, não me incomoda em linguagens como Java e C ++. As recursões em Java e C ++, quando aconteciam, geralmente eram tratadas com 2 a 3 funções no máximo. Nunca várias funções encadeadas como Scala.

Então, acho que estou me perguntando se existe um bom motivo para o Scala ter o requisito de funções com tipo de retorno explícito.

coleta de lixo
fonte
5
Não - e não vejo por que isso seria um problema.
perfil completo de Keith Thompson
1
Eu pensei que sim. Existem casos em Scala em que o tipo de retorno para uma função é realmente ambíguo?
coleta de lixo

Respostas:

15

Scala não requer um tipo de retorno explícito em todas as funções, apenas recursivas. A razão para isso é que o algoritmo de inferência de tipo da Scala é (algo próximo a) uma simples varredura do começo ao fim, que é incapaz de olhar de frente.

Isso significa que uma função como esta:

def fortuneCookieJoke(message: String) = message + " in bed."

não precisa de um tipo de retorno, uma vez que o compilador Scala pode ver claramente, sem usar variáveis ​​lógicas ou examinar nada além dos parâmetros do método, que o tipo de retorno deve ser String.

Por outro lado, uma função como esta:

def mapInts(f: (Int) => Int, l: List[Int]) = l match {
  case Nil => Nil
  case x :: xs => f(x) :: mapInts(f, xs)
}

causará um erro em tempo de compilação, porque o compilador Scala não pode ver, sem usar variáveis ​​lookahead ou lógicas, exatamente qual é o tipo mapInts. O máximo que se poderia dizer, se fosse inteligente o suficiente, é que o tipo de retorno é um supertipo List[Nothing], já que Nilé desse tipo. Isso não fornece informações suficientes para determinar com precisão o tipo de retorno mapInts.

Observe que isso é específico do Scala e que existem outras linguagens de tipo estaticamente (a maioria da família Miranda / Haskell / Clean, a maioria da família ML e algumas outras dispersas) que usam algoritmos de inferência de tipo muito mais abrangentes e capazes do que Scala usa. Além disso, esteja ciente de que isso não é inteiramente culpa da Scala; subtipagem nominal e inferência de tipo de módulo inteiro estão fundamentalmente em desacordo entre si, e os projetistas do Scala escolheram favorecer o primeiro em relação ao último por questões de compatibilidade com Java, enquanto as linguagens funcionais "mais puras" do tipo estaticamente foram projetadas principalmente com o escolha oposta em mente.

Chama de Ptharien
fonte
4
Na verdade, o problema não é tanto descobrir um algoritmo de dedução de tipo mais abrangente. Ele está descobrindo um algoritmo de dedução de tipo mais abrangente, mantendo a alta qualidade das mensagens de erro no compilador Scala atual.
Jörg W Mittag
1
O retorno correto para case Nilrealmente não estaria vazio List[Int]()? Nesse caso, um compilador suficientemente inteligente pode descobrir isso. Isso é tudo, no entanto, suponho que seja o advogado do diabo.
KChaloux