repl scala read-eval-print-loop exploratory

scala - repl ruby



Cómo investigar objetos/tipos/etc. de Scala REPL? (4)

Debes pasar el nombre de clase completo a javap .

Primero classOf usando classOf :

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

Luego use javap (no funciona desde repl para mí: ": javap no está disponible en esta plataforma"), por lo que el ejemplo es desde una línea de comando, en repl, creo que no es necesario especificar classpath:

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

Pero dudo que esto te ayude. Probablemente estás tratando de usar técnicas que utilizabas en lenguajes dinámicos. Raramente uso réplica en scala (aunque lo uso a menudo en javascript). Un IDE y las fuentes son mi todo.

He estado trabajando con Scala por un tiempo y he escrito un programa de más de 10,000 líneas, pero todavía estoy confundido por algunos de los trabajos internos. Llegué a Scala desde Python después de tener una familiaridad íntima con Java, C y Lisp, pero aun así ha sido lento, y un gran problema es la dificultad frustrante que a menudo he encontrado al intentar investigar el funcionamiento interno de los objetos / tipos. / clases / etc. utilizando el Scala REPL en comparación con Python. En Python puedes investigar cualquier objeto foo (tipo, objeto en una variable global, función incorporada, etc.) usando foo para ver qué se evalúa la cosa, type(foo) para mostrar su tipo, dir(foo) para decirle Puede conocer los métodos a los que puede recurrir y help(foo) a obtener la documentación integrada. Incluso puede hacer cosas como help("re") para encontrar la documentación en el paquete llamado re (que contiene objetos y métodos de expresiones regulares), aunque no haya ningún objeto asociado con él.

En Scala, puede intentar leer la documentación en línea, buscar el código fuente en la biblioteca, etc., pero a menudo esto puede ser muy difícil para las cosas en las que no sabe dónde ni qué es lo que están (y con frecuencia un pedazo grande para morder, dada la jerarquía de tipos voluminosos) - las cosas están flotando en varios lugares (paquete scala , Predef , varias conversiones implícitas, símbolos como :: que son casi imposibles para Google). El REPL debería ser la forma de explorar directamente, pero en realidad, las cosas son mucho más misteriosas. Digamos que he visto una referencia a foo algún lugar, pero no tengo idea de qué es. Aparentemente no existe tal cosa como una "guía para investigar sistemáticamente las cosas de Scala con el REPL", pero lo siguiente es lo que he reunido después de una gran cantidad de prueba y error:

  1. Si foo es un valor (que presumiblemente incluye cosas almacenadas en variables más objetos complementarios y otros object Scala), puede evaluar foo directamente. Esto debería decirle el tipo y el valor del resultado. A veces el resultado es útil, a veces no.
  2. Si foo es un valor, puede usar :type foo para obtener su tipo. (No necesariamente esclarecedor). Si usa esto en una llamada de función, obtiene el tipo del valor de retorno, sin llamar a la función.
  3. Si foo es un valor, puede usar foo.getClass para obtener su clase. (A menudo es más esclarecedor que el anterior, pero ¿en qué se diferencia la clase de un objeto de su tipo?)
  4. Para una clase foo , puedes usar classOf[foo] , aunque no es obvio lo que significa el resultado.
  5. Teóricamente, puedes usar :javap foo para desensamblar una clase, que debería ser la más útil de todas, pero para mí es un error total y uniforme.
  6. A veces tienes que juntar cosas de mensajes de error.

Ejemplo de fallo usando :javap :

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

Ejemplo de mensaje de error esclarecedor:

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 ^

Bien, ahora probemos un ejemplo simple.

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

Suficientemente simple ...

Ahora, probemos algunos casos reales, donde no es tan obvio:

scala> Predef res65: type = scala.Predef$@3cd41115 scala> :type Predef type scala> Predef.getClass res66: java.lang.Class[_ <: object Predef] = class scala.Predef$

¿Qué significa esto? ¿Por qué el tipo de Predef simple, mientras que la clase es scala.Predef$ ? Supongo que $ es la forma en que los objetos complementarios se encuadran en Java ... pero los documentos de Scala en Google me dicen que Predef es un object Predef extends LowPriorityImplicits : ¿cómo puedo deducir esto del REPL? ¿Y cómo puedo ver lo que hay en él?

OK, vamos a intentar otra cosa confusa:

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

De acuerdo, esto me dejó completamente confundido y, finalmente, tuve que leer el código fuente para entender todo esto.

Entonces, mis preguntas son:

  1. ¿Cuál es la mejor manera recomendada por los verdaderos expertos de Scala de usar el REPL para dar sentido a los objetos, clases, métodos, etc. de Scala, o al menos investigarlos lo mejor que se pueda hacer desde el REPL?
  2. ¿Cómo consigo :javap trabajando desde el REPL para cosas integradas? (¿No debería funcionar por defecto?)

Gracias por cualquier iluminación.


Javap funciona, pero lo estás apuntando a scala.Predef.List , que es un type , no una class . En su lugar, scala.collection.immutable.List a scala.collection.immutable.List .

Ahora, en su mayor parte, basta con ingresar un valor y ver cuál es el tipo del resultado. Usar :type puede ser útil a veces. Sin getClass me parece que el uso de getClass es una forma realmente mala de hacerlo.

Además, a veces estás mezclando tipos y valores. Por ejemplo, aquí te refieres al objeto ::

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

Y aquí te refieres a la clase ::

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

Los objetos y las clases no son lo mismo y, de hecho, hay un patrón común de objetos y clases con el mismo nombre, con un nombre específico para su relación: compañeros.

En lugar de dir , solo usa la tabulación:

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 ingresas al modo de energía, obtendrás mucha más información, pero eso no es para principiantes. Lo anterior muestra tipos, métodos y firmas de métodos. Javap descompilará cosas, aunque eso requiere que tengas un buen manejo del código de bytes.

Hay otras cosas ahí dentro, asegúrese de buscar :help , y vea qué hay disponible.

Los documentos solo están disponibles a través de la API de scaladoc. Manténgalo abierto en el navegador y use su capacidad de búsqueda para encontrar clases y métodos rápidamente. Además, tenga en cuenta que, a diferencia de Java, no necesita navegar a través de la lista de herencia para obtener la descripción del método.

Y buscan perfectamente bien los símbolos. Sospecho que no ha pasado mucho tiempo en scaladoc porque otras herramientas de documentación no están a la altura. Javadoc viene a la mente: es horrible navegar a través de paquetes y clases.

Si tiene preguntas específicas sobre el estilo de desbordamiento de pila, use Symbol Hound para buscar con símbolos.

Use los Scaladocs nightly : divergirán de cualquier versión que esté usando, pero siempre serán las más completas. Además, en este momento son mucho mejores en muchos aspectos: puede usar la tecla TAB para alternar entre cuadros, con el enfoque automático en los cuadros de búsqueda, puede usar flechas para navegar en el cuadro de la izquierda después del filtrado y ENTER para tener el elemento seleccionado Aparecen en el cuadro de la derecha. Tienen la lista de métodos implícitos, y tienen diagramas de clase.

Me he conformado con un REPL mucho menos poderoso y un Scaladoc mucho más pobre: ​​trabajan juntos. Por supuesto, me salté al baúl (ahora HEAD) solo para poner mis manos en la tabulación.


Mencionaste un punto importante del que Scala carece un poco: la documentación.

El REPL es una herramienta fantástica, pero no es tan fantástico como puede serlo. Hay demasiadas características faltantes y características que se pueden mejorar, algunas de ellas se mencionan en tu publicación. Scaladoc también es una buena herramienta, pero está muy lejos de ser perfecto. Además, una gran cantidad de código en la API no está aún documentado o está demasiado documentado y, a menudo, faltan ejemplos de código. Los IDE son errores completos y, en comparación con las posibilidades, los IDE de Java nos muestran que se parecen a algunos juguetes de jardín de infantes.

Sin embargo, hay una diferencia gigantesca entre las herramientas actuales de Scalas en comparación con las herramientas disponibles cuando comencé a aprender Scala hace 2 o 3 años. En ese momento, los IDE compilaban permanentemente algo de basura en el fondo, el compilador fallaba cada pocos minutos y algo de documentación era absolutamente inexistente. Con frecuencia recibí ataques de rabia y deseé muerte y corrupción a los autores de Scala.

¿Y ahora? Ya no tengo ninguno de estos ataques de rabia. ¡Porque las herramientas que tenemos actualmente son geniales, aunque no son perfectas!

Hay docs.scala-lang.org , que resume una gran cantidad de documentación excelente. Hay tutoriales, hojas de trucos, glosarios, guías y muchas más cosas geniales. Otra gran herramienta es Scalex , que puede encontrar hasta el operador más extraño que se pueda imaginar. Es Scalas Hoogle y aunque no es tan bueno como su gran ideal, es muy útil.

Las grandes mejoras vienen con Scala2.10 en forma de la propia biblioteca de Reflexión de Scalas:

// 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 documentación para la nueva biblioteca de Reflection todavía falta, pero está en progreso. Permite usar scalac de una manera fácil dentro del 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$1) => x$1.$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$1) => x$1.$plus(1))) scala> tb.runExpr(res16) res18: Any = List(2, 3, 4)

Esto es aún mayor cuando queremos saber cómo se traduce internamente el código Scala. Anteriormente, cuando era necesario escribir scala -Xprint:typer -e "List(1,2,3) map (_+1)" para obtener la representación interna. Además, algunas pequeñas mejoras encontraron su camino hacia la nueva versión, por ejemplo:

scala> :type Predef scala.Predef.type

Scaladoc obtendrá un gráfico de jerarquía de tipos (haga clic en la jerarquía de tipos).

Con Macros es posible ahora, mejorar los mensajes de error de una manera excelente. Hay una biblioteca llamada expecty , que hace esto:

// 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) */

Existe una herramienta que permite encontrar bibliotecas alojadas en GitHub, llamada ls.implicit.ly .

Los IDE ahora tienen un resaltado semántico, para mostrar si un miembro es un objeto / tipo / método / lo que sea. La característica de resaltado semántico de ScalaIDE .

La función javap del REPL es solo una llamada al javap nativo, por lo que no es una herramienta muy rica en características. Tienes que calificar completamente el nombre de un módulo:

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{ ...

Hace algún tiempo, he escrito un resumen de cómo se compila el código Scala en Bytecode , que ofrece muchas cosas que saber.

Y lo mejor: ¡todo esto se ha hecho en los últimos meses!

Entonces, ¿cómo usar todas estas cosas dentro del REPL? Bueno, no es posible ... todavía no. ;)

Pero puedo decirles que un día tendremos un REPL. Un REPL que nos muestra la documentación si queremos verla. Un REPL que nos permite comunicarnos con él (tal vez como lambdabot ). Un REPL que nos permite hacer cosas geniales que aún no podemos imaginar. No sé cuándo será este el caso, pero sé que se hicieron muchas cosas en los últimos años y sé que se harán cosas aún mejores en los próximos años.


Tenga en cuenta que scala 2.11.8 La finalización de una nueva pestaña en el REPL de Scala puede facilitar el tipo de exploración / descubrimiento.

Ahora incluye:

  • Finalización de CamelCase:
    tratar:
    (l: List[Int]).rro TAB ,
    se expande a:
    (l: List[Int]).reduceRightOption

  • Encuentra miembros escribiendo cualquier parte del nombre de CamelCased:
    tratar:
    classOf[String].typ TAB , para obtener getAnnotationsByType , getComponentType y otros

  • Obtiene getters de frijoles completos sin escribir:
    tratar:
    (d: java.util.Date).day TAB

  • Presione TAB dos veces para ver la firma del método:
    tratar:
    List(1,2,3).part TAB ,
    que completa a:
    List(1,2,3).partition ;
    presiona TAB otra vez para mostrar:
    def partition(p: Int => Boolean): (List[Int], List[Int])