Pergunta

Usando REPL linha de comando do Scala:

def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}

error: type mismatch;
found: Int(2)
required: String

Parece que você não pode definir métodos sobrecarregados recursiva no REPL. Eu pensei que era um erro no Scala REPL e arquivado, mas foi quase imediatamente fechada com "wontfix: Eu não vejo nenhuma maneira isso poderia ser apoiada dada a semântica do intérprete, porque estes dois métodos devem ser compilados juntos." Ele recomendou colocar os métodos em um objeto de fechamento.

Existe uma implementação da linguagem JVM ou perito Scala que poderia explicar por quê? Eu posso ver que seria um problema se os métodos chamavam uns aos outros, por exemplo, mas neste caso?

ou se esta é a pergunta de um muito grande e você acha que eu preciso mais conhecimento pré-requisito, é que alguém tem alguma boa links para livros ou sites sobre implementações de linguagem, especialmente na JVM? (Eu sei sobre o blog do John Rose, eo livro Programação Pragmática Idioma ... mas é sobre isso:.)

Foi útil?

Solução

O problema é devido ao fato de que o intérprete na maioria das vezes tem que substituir elementos existentes com um determinado nome, em vez de sobrecarregá-los. Por exemplo, eu, muitas vezes, estar em execução através da experimentação com algo, muitas vezes criando um método chamado test:

def test(x: Int) = x + x

Um pouco mais tarde, vamos dizer que eu estou correndo um diferente experiência e eu criar um outro método chamado test, sem relação com o primeiro:

def test(ls: List[Int]) = (0 /: ls) { _ + _ }

Este não é um cenário completamente irreal. Na verdade, é exatamente como a maioria das pessoas utilizar o intérprete, muitas vezes, mesmo sem perceber. Se o intérprete decidiu arbitrariamente para manter ambas as versões do test em escopo, que podem levar a confundir diferenças semânticas no uso de teste. Por exemplo, poderíamos fazer uma chamada para test, acidentalmente passar um Int em vez de List[Int] (não o acidente mais improvável do mundo):

test(1 :: Nil)  // => 1
test(2)         // => 4  (expecting 2)

Com o tempo, o escopo raiz do intérprete ficaria incrivelmente confuso com várias versões de métodos, campos, etc. I tendem a deixar o meu aberta intérprete para dias de cada vez, mas se sobrecarregando assim foram autorizados, estaríamos forçada a "lavar" o intérprete a cada tantas vezes como as coisas tem que ser muito confuso.

Não é uma limitação do JVM ou o compilador Scala, é uma decisão de projeto deliberado. Como mencionado no erro, você ainda pode sobrecarregar se você estiver dentro de algo que não seja o escopo raiz. Encerrando seus métodos de ensaio dentro de uma classe parece ser a melhor solução para mim.

Outras dicas

% scala28
Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def foo(x: Int): Unit = () ; def foo(x: String): Unit = { println(foo(2)) } 
foo: (x: String)Unit <and> (x: Int)Unit
foo: (x: String)Unit <and> (x: Int)Unit

scala> foo(5)

scala> foo("abc")
()

REPL aceitará se você copiar ambas as linhas e cole ambos ao mesmo tempo.

Como mostrado pela resposta de improviso, é possível sobrecarga. de href="https://stackoverflow.com/users/9815/daniel-spiewak"> Daniel sobre a decisão de design está correto, mas, penso eu, incompleta e um pouco enganador. Não há nenhuma ilegalização de sobrecargas (uma vez que eles são possíveis), mas eles não são facilmente alcançados.

As decisões de design que levam a este são:

  1. Todas as definições anteriores deve estar disponível.
  2. Somente o código recém-introduzido é compilado, em vez de recompilar tudo já entrou cada vez.
  3. Deve ser possível redefinir as definições (como Daniel mencionado).
  4. Deve ser possível definir membros como vals e defs, não apenas as classes e objetos.

O problema é ... como alcançar todos estes objetivos? Como é que vamos processar o seu exemplo?

def foo(x: Int): Unit = {}
def foo(x: String): Unit = {println(foo(2))}

A partir do 4º item, A val ou def só pode ser definida dentro de uma class, trait, object ou package object. Então, coloca REPL as definições dentro de objetos, como este ( não representação real! )

package $line1 { // input line
  object $read { // what was read
    object $iw { // definitions
      def foo(x: Int): Unit = {}
    }
    // val res1 would be here somewhere if this was an expression
  }
}

Agora, devido à forma como JVM funciona, uma vez que você definiu um deles, você não pode estendê-los. Você poderia, é claro, tudo recompilação, mas nós descartamos isso. Então, você precisa colocá-lo em um lugar diferente:

package $line1 { // input line
  object $read { // what was read
    object $iw { // definitions
      def foo(x: String): Unit = { println(foo(2)) }
    }
  }
}

E isso explica por que seus exemplos não são sobrecargas: eles são definidos em dois lugares diferentes. Se você colocá-los na mesma linha, todos estariam definidos em conjunto, o que os tornaria sobrecargas, como mostrado no exemplo de improviso.

Como para as outras decisões de design, cada nova definições pacote de importação e "res" de pacotes anteriores, e as importações podem sombra uns aos outros, o que torna possível a coisas "redefinir".

scroll top