Question

Utilisation de la ligne de commande Scala REPL:

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

donne

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

Il semble que vous ne puissiez pas définir de méthodes récursives surchargées dans le REPL. Je pensais qu'il s'agissait d'un bogue dans le REPL Scala et je l'ai classé, mais il a été presque instantanément fermé avec "wontfix: je ne vois aucun moyen de le supporter compte tenu de la sémantique de l'interpréteur, car ces deux méthodes doivent être compilés ensemble. " Il a recommandé de placer les méthodes dans un objet englobant.

Existe-t-il un expert en implémentation du langage JVM ou en Scala qui pourrait expliquer pourquoi? Je peux voir que ce serait un problème si les méthodes s’appelaient par exemple, mais dans ce cas?

Ou si la question est trop vaste et que vous pensez que j'ai besoin de davantage de connaissances préalables, quelqu'un at-il de bons liens vers des livres ou des sites sur les implémentations linguistiques, en particulier sur la JVM? (Je connais le blog de John Rose et le livre Programming Language Pragmatics ... mais c'est à peu près tout.)

Était-ce utile?

La solution

Le problème est dû au fait que l'interprète doit le plus souvent remplacer les éléments existants par un nom donné, plutôt que de les surcharger. Par exemple, je vais souvent expérimenter quelque chose, en créant souvent une méthode appelée test :

def test(x: Int) = x + x

Un peu plus tard, supposons que j'exécute une expérience différente et que je crée une autre méthode nommée test , sans rapport avec la première:

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

Ce scénario n’est pas totalement irréaliste. En fait, c’est précisément la façon dont la plupart des gens utilisent l’interprète, souvent sans même s’en rendre compte. Si l'interprète décidait de manière arbitraire de conserver les deux versions de test , cela pourrait entraîner des différences sémantiques confuses dans l'utilisation de test. Par exemple, nous pourrions faire appel à test , en passant accidentellement un Int plutôt que List [Int] (l'accident le plus improbable de la monde):

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

Au fil du temps, la portée principale de l'interprète devenait incroyablement encombrée de versions différentes de méthodes, de champs, etc. J'ai tendance à laisser mon interprète ouvert plusieurs jours à la fois, mais si une telle surcharge était autorisée, nous serions forcé à " rincer " l’interprète de temps en temps car les choses doivent être trop déroutantes.

Ce n'est pas une limitation de la JVM ou du compilateur Scala, c'est une décision de conception délibérée. Comme mentionné dans le bogue, vous pouvez toujours surcharger si vous êtes dans autre chose que la portée racine. Enfermer vos méthodes de test dans une classe me semble être la meilleure solution.

Autres conseils

% 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 acceptera si vous copiez les deux lignes et les collez en même temps.

Comme le montre la réponse d’empempore , il est possible de surcharger. Le commentaire de Daniel sur la décision de conception est correct, mais incomplet et un peu trompeur à mon avis. Il n’existe pas d'interdictions de surcharges (car elles sont possibles), mais elles ne sont pas facilement réalisées.

Les décisions de conception qui en découlent sont les suivantes:

  1. Toutes les définitions précédentes doivent être disponibles.
  2. Seul le code nouvellement entré est compilé, au lieu de tout recompiler à chaque fois.
  3. Il doit être possible de redéfinir les définitions (comme l'a mentionné Daniel).
  4. Il doit être possible de définir des membres tels que vals et defs, pas seulement des classes et des objets.

Le problème est ... comment atteindre tous ces objectifs? Comment traitons-nous votre exemple?

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

À partir du 4ème élément, un val ou un def ne peut être défini qu'à l'intérieur d'une classe , trait . , objet ou objet de package . Ainsi, REPL place les définitions à l'intérieur des objets, comme ceci ( pas de représentation réelle! )

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
  }
}

Maintenant, en raison du fonctionnement de la machine virtuelle Java, une fois que vous en avez défini une, vous ne pouvez pas les étendre. Vous pouvez bien sûr tout recompiler, mais nous l’avons écarté. Vous devez donc le placer dans un endroit différent:

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

Et ceci explique pourquoi vos exemples ne sont pas des surcharges: ils sont définis à deux endroits différents. Si vous les mettez dans la même ligne, ils seront tous définis ensemble, ce qui les rendrait surchargés, comme le montre l'exemple d'extempore.

Comme pour les autres décisions de conception, chaque nouvelle définition d'importation de package et "res". à partir de paquets précédents, et les importations peuvent se masquer, ce qui permet de "redéfinir" des trucs.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top