Comment enquêter sur les objets/types / etc. de Scala REPL?

je travaille avec Scala depuis un certain temps maintenant et j'ai écrit un programme de plus de 10 000 lignes avec, mais je suis encore confus par certains des travaux intérieurs. Je suis venu à Scala de Python après avoir déjà eu une familiarité intime avec Java, C et Lisp, mais même si cela a été lent, et un énorme problème est la difficulté frustrante que j'ai souvent trouvé en essayant d'enquêter sur le fonctionnement interne des objets/types/classes/etc. utilisation de la REPL Scala par rapport à Python. En Python vous pouvez rechercher tout objet foo (type, objet dans une variable globale, fonction intégrée, etc.) en utilisant foo pour voir ce que la chose évalue, type(foo) pour montrer son type, dir(foo) pour vous dire les méthodes que vous pouvez faire appel à elle, et help(foo) pour obtenir la documentation intégrée. Vous pouvez même faire des choses comme help("re") pour trouver la documentation sur le paquet nommé re (qui contient des objets et des méthodes d'expression régulière), même s'il n'y a pas d'objet associé avec elle.

en Scala, vous pouvez essayer de lire la documentation en ligne, allez chercher le code source à la bibliothèque, etc., mais cela peut souvent être très difficile pour les choses où vous ne savez pas où ou même ce qu'ils sont (et c'est souvent un gros morceau à mordre, étant donné la volumineuse hiérarchie de type) -- les choses flottent autour de divers endroits (paquet scala , Predef , diverses conversions implicites, les symboles comme :: qui sont presque impossible de Google). La REPL devrait être le moyen d'Explorer directement, mais en réalité, les choses sont beaucoup plus mystérieuses. Dire que j'ai vu une référence à foo quelque part, mais je n'ai aucune idée de ce que c'est. Apparemment, il n'existe pas de "guide pour enquêter systématiquement sur les Scalas avec le REPL", mais ce qui suit est ce que j'ai assemblé après beaucoup d'essais et d'erreurs:

  1. si foo est une valeur (qui inclut des choses stockées dans des variables plus compagnon des objets et d'autres Scala object s), vous pouvez évaluer foo directement. Cela devrait vous dire le type et la valeur du résultat. Parfois, le résultat est utile, parfois pas.
  2. si foo est une valeur, vous pouvez utiliser :type foo pour obtenir son type. (Pas nécessairement instructif.) Si vous utilisez cela sur un appel de fonction, vous obtenez le type de la valeur de retour, sans appeler la fonction.
  3. si foo est une valeur, vous pouvez utiliser foo.getClass pour obtenir sa classe. (Souvent plus instructif que le précédent, mais en quoi la classe d'un objet diffère-t-elle de son type?)
  4. pour une classe foo , vous pouvez utiliser classOf[foo] , bien qu'il ne soit pas évident ce que le résultat signifie.
  5. théoriquement, vous pouvez utiliser :javap foo pour démonter une classe -- qui devrait être la plus utile de toutes, mais qui échoue entièrement et uniformément pour moi.
  6. parfois, vous devez rassembler les choses des messages d'erreur.

exemple de défaillance utilisant :javap :

scala> :javap List
Failed: Could not find class bytes for 'List'

exemple de message d'erreur éclairant:

scala> assert
<console>:8: error: ambiguous reference to overloaded definition,
both method assert in object Predef of type (assertion: Boolean, message: => Any)Unit
and  method assert in object Predef of type (assertion: Boolean)Unit
match expected type ?
              assert
              ^

OK, maintenant essayons un exemple simple.

scala> 5
res63: Int = 5

scala> :type 5
Int

scala> 5.getClass
res64: java.lang.Class[Int] = int

assez Simple ...

Maintenant, essayons quelques cas réels, où il n'est pas si évident:

scala> Predef
res65: type = scala.Predef$@3cd41115

scala> :type Predef
type

scala> Predef.getClass
res66: java.lang.Class[_ <: object Predef] = class scala.Predef$

Qu'est-ce que cela signifie? Pourquoi le type de Predef est-il simplement type , alors que la classe est scala.Predef$ ? Je crois comprendre que le $ est la façon dont les objets compagnons sont rangés dans Java ... mais Scala docs sur Google me dire que Predef est object Predef extends LowPriorityImplicits -- Comment puis-je en déduire de la REPL? Et comment puis-je regarde ce qui est en elle?

OK, essayons autre chose confuse:

scala> `::`
res77: collection.immutable.::.type = ::

scala> :type `::`
collection.immutable.::.type

scala> `::`.getClass
res79: java.lang.Class[_ <: object scala.collection.immutable.::] = class scala.collection.immutable.$colon$colon$

scala> classOf[`::`]
<console>:8: error: type :: takes type parameters
              classOf[`::`]
                      ^

scala> classOf[`::`[Int]]
res81: java.lang.Class[::[Int]] = class scala.collection.immutable.$colon$colon

OK, cela m'a laissé désespérément confus, et finalement j'ai dû aller lire le code source pour donner un sens à tout cela.

donc, mes questions sont:

  1. Quelle est la meilleure façon recommandée par les vrais experts de Scala d'utiliser la REPL pour donner un sens aux objets, classes, méthodes Scala, etc., ou au moins les étudier du mieux que peut être fait à partir de la RÉPL?
  2. Comment puis-je obtenir :javap travailler à partir de la RÉPL pour des trucs intégrés? (Ne devrait-il pas fonctionner par défaut?)

Merci pour toute illumination.

41
demandé sur kiritsuku 2012-07-09 13:47:24

4 réponses

vous avez mentionné un point important qui manque un peu à Scala: la documentation.

Le REPL est un outil fantastique, mais il n'est pas fantastique, il peut être. Il y a trop de traits manquants et des traits qui peuvent être améliorés - certains d'entre eux sont mentionnés dans votre post. Scaladoc est un bel outil, aussi, mais est loin d'être parfait. De plus, beaucoup de code dans L'API n'est pas encore ou trop peu documenté et les exemples de code sont souvent absents. Les IDEs sont des insectes ob pleins et par rapport aux possibilités Java IDEs nous montrent qu'ils ressemblent à des jouets de maternelle.

néanmoins, il y a une différence énorme entre les outils courants de Scalas et les outils disponibles lorsque j'ai commencé à apprendre le Scala il y a 2-3 ans. A ce moment-là, IDEs a compilé de façon permanente quelques déchets à l'arrière-plan, le compilateur s'est écrasé toutes les quelques minutes et il n'y avait absolument aucune documentation. Souvent j'ai eu des attaques de rage et souhaité la mort et la corruption à Scala auteurs.

et maintenant? Je n'ai plus aucune de ces attaques de rage. Parce que les outils que nous avons actuellement sont grands, bien le ne sont pas parfaits!

il y a docs.scala-lang.org , qui résume beaucoup de grande documentation. Il y a des tutoriels, des fiches de tricherie, des glossaires, des Guides et beaucoup d'autres choses géniales. Un autre grand outil est Scalex , qui peut trouver même l'opérateur le plus étrange que l'on peut penser. C'est Scalas Hoogle et bien qu'il ne soit pas encore aussi bon que son grand idéal, il est très utile.

de grandes améliorations sont à venir avec Scala2.10 sous forme de Scalas propre bibliothèque de réflexion:

// needs Scala2.10M4
scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}

scala> val t = u.typeOf[List[_]]
t: reflect.runtime.universe.Type = List[Any]

scala> t.declarations
res10: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(constructor List, method companion, method isEmpty, method head, method tail, method ::, method :::, method reverse_:::, method mapConserve, method ++, method +:, method toList, method take, method drop, method slice, method takeRight, method splitAt, method takeWhile, method dropWhile, method span, method reverse, method stringPrefix, method toStream, method removeDuplicates)

la Documentation pour la nouvelle bibliothèque de réflexion est toujours manquante, mais en cours. Il permet d'utiliser scalac d'une manière simple à l'intérieur de la REPL:

scala> u reify { List(1,2,3) map (_+1) }
res14: reflect.runtime.universe.Expr[List[Int]] = Expr[List[Int]](immutable.this.List.apply(1, 2, 3).map(((x) => x.$plus(1)))(immutable.this.List.canBuildFrom))

scala> import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBox

scala> import scala.reflect.runtime.{currentMirror => m}
import scala.reflect.runtime.{currentMirror=>m}

scala> val tb = m.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@32f7fa37

scala> tb.parseExpr("List(1,2,3) map (_+1)")
res16: tb.u.Tree = List(1, 2, 3).map(((x) => x.$plus(1)))

scala> tb.runExpr(res16)
res18: Any = List(2, 3, 4)

C'est encore plus quand nous voulons savoir comment le code Scala est traduit en interne. Auparavant, Wen doit taper scala -Xprint:typer -e "List(1,2,3) map (_+1)" pour obtenir la représentation interne. En outre, quelques petites améliorations ont trouvé place à la nouvelle version, par exemple:

scala> :type Predef
scala.Predef.type

Scaladoc gagnera quelques graphe de type-hiérarchie (cliquez sur Type-hiérarchie).

avec les Macros il est maintenant possible d'améliorer les messages d'erreur d'une grande manière. Il y a une bibliothèque appelée expecty , qui fait ceci:

// copied from GitHub page
import org.expecty.Expecty

case class Person(name: String = "Fred", age: Int = 42) {
  def say(words: String*) = words.mkString(" ")
}

val person = Person()
val expect = new Expecty()

// Passing expectations

expect {
  person.name == "Fred"
  person.age * 2 == 84
  person.say("Hi", "from", "Expecty!") == "Hi from Expecty!"
}

// Failing expectation

val word1 = "ping"
val word2 = "pong"

expect {
  person.say(word1, word2) == "pong pong"
}

/*
Output:

java.lang.AssertionError:

person.say(word1, word2) == "pong pong"
|      |   |      |      |
|      |   ping   pong   false
|      ping pong
Person(Fred,42)
*/

il y a un outil qui permet de trouver des bibliothèques hébergées sur GitHub, appelé ls.implicite.ly .

les IDEs ont maintenant une certaine mise en évidence sémantique, pour montrer si un membre est un objet/type/Méthode/n'importe quoi. La caractéristique de mise en évidence sémantique de ScalaIDE .

la fonctionnalité javap de la javap natif, donc ce n'est pas un outil très riche en fonctionnalités. Vous devez pleinement qualifier le nom d'un module:

scala> :javap scala.collection.immutable.List
Compiled from "List.scala"
public abstract class scala.collection.immutable.List extends scala.collection.AbstractSeq implements scala.collection.immutable.LinearSeq,scala.Product,scala.collection.LinearSeqOptimized{
...

Il ya quelque temps , j'ai écrit un résumé de la façon dont le code Scala est compilé en Bytecode , qui offre beaucoup de choses à savoir.

Et le meilleur: C'est tout à fait dans les derniers mois!

alors, comment utiliser toutes ces choses à l'intérieur de la RÉPL? Eh bien, il n'est pas possible ... pas encore de. ;)

mais je peux vous dire qu'un jour nous aurons une telle RÉPL. Une réponse qui nous montre la documentation si nous voulons la voir. Une réponse qui nous permet de communiquer avec elle (peut-être comme lambdabot ). Une réponse qui nous laisse faire des choses cool que nous ne pouvons toujours pas imaginer. Je ne sais pas quand ce sera le cas, mais je sais que beaucoup de choses ont été faites dans les dernières années et je sais même plus de choses seront faites dans les prochaines années.

32
répondu kiritsuku 2017-05-23 12:09:53

Javap fonctionne , mais vous pointez vers scala.Predef.List , qui est un type , pas un class . Pointez-le plutôt sur scala.collection.immutable.List .

maintenant, pour la plupart, il suffit d'entrer une valeur et de voir ce qu'est le type du résultat. Utiliser :type peut être utile parfois. Je trouve que l'utilisation getClass est une très mauvaise façon de le faire, cependant.

aussi, vous mélangez parfois des types et des valeurs. Par exemple, ici, vous se référer à l'objet :: :

scala> `::`.getClass res79: java.lang.Class[_ <: object
scala.collection.immutable.::] = class
scala.collection.immutable.$colon$colon$

et ici vous vous référez à la classe :: :

scala> classOf[`::`[Int]] res81: java.lang.Class[::[Int]] = class
scala.collection.immutable.$colon$colon

les objets et les classes ne sont pas la même chose, et, en fait, il y a un modèle commun d'objets et de classes par le même nom, avec un nom spécifique pour leur relation: compagnons.

au lieu de dir , il suffit d'utiliser l'achèvement de l'onglet:

scala> "abc".
+                     asInstanceOf          charAt                codePointAt           codePointBefore       codePointCount
compareTo             compareToIgnoreCase   concat                contains              contentEquals         endsWith
equalsIgnoreCase      getBytes              getChars              indexOf               intern                isEmpty
isInstanceOf          lastIndexOf           length                matches               offsetByCodePoints    regionMatches
replace               replaceAll            replaceFirst          split                 startsWith            subSequence
substring             toCharArray           toLowerCase           toString              toUpperCase           trim

scala> "abc".compareTo
compareTo             compareToIgnoreCase

scala> "abc".compareTo
                             def compareTo(String): Int

Si vous entrez le pouvoir mode, vous obtiendrez beaucoup plus d'informations, mais ce est à peine pour les débutants. Ce qui précède montre les types, les méthodes et les signatures de méthode. Javap va décompiler des trucs, bien que cela nécessite que vous ayez un bon contrôle sur bytecode.

il y a d'autres choses là-dedans -- assurez-vous de chercher :help , et de voir ce qui est disponible.

Les Docs

ne sont disponibles que via l'API scaladoc. Gardez-le ouvert sur le navigateur, et utilisez son recherche capacité de trouver rapidement les classes et les méthodes. De plus, notez que, contrairement à Java, vous n'avez pas besoin de naviguer dans la liste d'héritage pour obtenir la description de la méthode.

et ils recherchent parfaitement les symboles. Je soupçonne que vous n'avez pas passé beaucoup de temps sur scaladoc parce que les autres outils doc là-bas ne sont pas à la hauteur. Javadoc vient à l'esprit -- c'est terrible de parcourir les paquets et les classes.

si vous avez des questions spécifiques Pour effectuer une recherche à l'aide de symboles, utilisez symbole Hound .

utilisez le nightly Scaladocs: ils divergeront de la version que vous utilisez, mais ils seront toujours les plus complets. En outre, en ce moment, ils sont beaucoup mieux à bien des égards: vous pouvez utiliser TAB pour alterner entre les cadres, avec auto-focus sur les boîtes de recherche, vous pouvez utiliser les flèches pour naviguer sur le cadre de gauche après le filtrage, et ENTRER pour avoir le l'élément sélectionné apparaît sur le cadre de droite. Ils ont la liste des implicites méthodes, et ont des diagrammes de classe.

j'ai fait avec un REPL beaucoup moins puissant, et un Scaladoc beaucoup plus pauvre -- ils travaillent, ensemble. D'accord, j'ai sauté dans le coffre (maintenant tête) juste pour mettre la main sur tab-completion.

5
répondu Daniel C. Sobral 2012-07-09 20:58:17

noter que scala 2.11.8 nouveau tab-completion in the Scala REPL peut faciliter le type exploration/découverte.

comprend maintenant:

  • CamelCase la fin:

    essayez:

    (l: List[Int]).rro ONGLET ,

    il s'étend à:

    (l: List[Int]).reduceRightOption

  • trouver les membres en tapant n'importe quelle partie en couleur du nom:

    essaie.:

    classOf[String].typ TAB , pour obtenir getAnnotationsByType , getComponentType et autres

  • Compléter le haricot getters sans taper get:

    essayez:

    (d: java.util.Date).day ONGLET

  • presse TAB deux fois pour voir la méthode signature:

    essayez:

    List(1,2,3).part ONGLET ,

    qui complète à:

    List(1,2,3).partition ;

    appuyez sur TAB pour afficher à nouveau:

    def partition(p: Int => Boolean): (List[Int], List[Int])

4
répondu VonC 2016-03-14 08:30:06

vous devez passer le nom de classe pleinement qualifié à javap .

prenez D'abord classOf :

scala> classOf[List[_]]
res2: java.lang.Class[List[_]] = class scala.collection.immutable.List

puis utiliser javap (ne fonctionne pas de repl pour moi:": javap indisponible sur cette plate-forme.") donc l'exemple vient d'une ligne de commande, en repl, je crois, vous n'avez pas besoin de spécifier classpath:

d:\bin\scala\scala-2.9.1-1\lib>javap -classpath scala-library.jar "scala.collection.immutable.List"

mais je doute que cela vous aide. Vous essayez probablement d'utiliser des techniques que vous utilisiez. en langues dynamiques. J'utilise très rarement repl en scala (alors que je l'utilise souvent en javascript). Un IDE et des sources sont tout pour moi.

2
répondu Yaroslav 2012-07-09 10:53:07