Как исследовать объекты / типы / etc. из Scala REPL?

я уже некоторое время работаю со Scala и написал с ним программу линии 10,000+, но я все еще смущен некоторыми внутренними работами. Я пришел в Scala из Python после того, как уже имел близкое знакомство с Java, C и Lisp, но даже так это было медленно, и огромная проблема-это разочаровывающая трудность, которую я часто находил при попытке исследовать внутреннюю работу объектов/типов/классов/и т. д. использование Scala REPL по сравнению с Python. В Python вы можете исследовать любой объект foo (тип, объект в глобальной переменной, встроенная функция и т. д.) с помощью foo чтобы увидеть, что дело принимает, type(foo) чтобы показать свою типа dir(foo) чтобы рассказать вам методы, которые вы можете вызвать на нем, и help(foo) чтобы получить встроенную документацию. Вы даже можете делать такие вещи, как help("re") найти документацию на пакет re (который содержит объекты и методы регулярного выражения), даже если нет объекта, связанного с он.

в Scala вы можете попробовать прочитать документацию в интернете,найти исходный код в библиотеке и т. д., но это часто может быть очень сложно для вещей, где вы не знаете, где или даже что они (и часто это большой кусок, чтобы откусить, учитывая объемную иерархию типов) - вещи плавают в разных местах (package scala, Predef различные неявные преобразования, символы, такие как :: это почти невозможно для Google). REPL должен быть способ исследовать напрямую, но на самом деле вещи гораздо более загадочны. Скажите, что я видел ссылку на

  1. если foo - это значение (которое предположительно включает вещи, хранящиеся в переменных плюс сопутствующие объекты и другие Scala objects), Вы можете оценить foo напрямую. Это должно сказать вам тип и значение результата. Иногда результат полезен, иногда нет.
  2. если foo - это значение, вы можете использовать :type foo получить свой тип. (Не обязательно полезным.) Если вы используете это при вызове функции, вы получаете тип возвращаемого значения без вызова функции.
  3. если foo - это значение, вы можете использовать foo.getClass чтобы получить свой класс. (Часто более информативный, чем предыдущий, но чем класс объекта отличается от его типа?)
  4. класс foo, вы можете использовать classOf[foo], хотя не очевидно, что означает результат.
  5. теоретически, вы можете использовать :javap foo разобрать класс , который должен быть самым полезным из всех, но не полностью и равномерно для меня.
  6. иногда вам приходится собирать вещи из сообщений об ошибках.

пример использования сбоя :javap:

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

пример просветляющего сообщения об ошибке:

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
              ^

ОК, теперь давайте попробуем простой пример.

scala> 5
res63: Int = 5

scala> :type 5
Int

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

достаточно просто ...

теперь давайте попробуем некоторые реальные случаи, где это не так очевидно:

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

scala> :type Predef
type

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

что это значит? Почему тип Predef просто type, тогда как класс scala.Predef$? Я полагаю, что $ - это способ, которым сопутствующие объекты загружаются в Java ... но Scala docs в Google скажите мне, что Predef is object Predef extends LowPriorityImplicits -- как я могу вывести это из REPL? И как я могу заглянуть в него?

хорошо, давайте попробуем еще одну запутанную вещь:

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

ОК, это оставило меня безнадежно спутаны, и в конце концов мне пришлось прочитать исходный код, чтобы понять все это.

Итак, мои вопросы:

  1. каков рекомендуемый лучший способ от истинных экспертов Scala использовать REPL, чтобы понять Scala объекты, классы, методы и т. д. или, по крайней мере, исследовать их как можно лучше из REPL?
  2. как сделать :javap работа от REPL для встроенного материала? (Разве это не должно работать по умолчанию?)

Спасибо за любое просветление.

4 ответов


Вы упомянули важный момент, которого Scala немного не хватает: документация.

REPL-фантастический инструмент, но он не так фантастичен, как может быть. Слишком много недостающих функций и функций, которые можно улучшить - некоторые из них упоминаются в вашем посте. Scaladoc-хороший инструмент, но он далек от совершенства. Кроме того, много кода в API еще не задокументировано или слишком мало, и примеры кода часто отсутствуют. IDEs полны ошибок ob и по сравнению с возможности Java IDEs показывают нам, что они выглядят как некоторые детские игрушки.

тем не менее существует гигантская разница Scalas текущих инструментов по сравнению с инструментами, доступными, как я начал изучать Scala 2-3 лет назад. В то время IDEs постоянно компилировал какой-то мусор в фоновом режиме, компилятор разбивался каждые несколько минут, а какая-то документация была абсолютно несуществующей. Часто я получал приступы ярости и желал смерти и коррупции авторам Scala.

и сейчас? У меня больше нет этих приступов ярости. Потому что инструменты, которые у нас в настоящее время есть, велики, хотя и не идеальны!

есть docs.scala-lang.org, который суммирует много большой документации. Есть учебники, шпаргалки, глоссарии, руководства и многое другое. Еще один отличный инструмент -свойства scaleX, который может найти даже самый странный оператор, о котором можно подумать. Это Скалас Hoogle и даже если это еще не так хорош, как его великий идеал, он очень полезен.

большие улучшения идут с Scala2.10 в виде скаляров собственная библиотека отражений:

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

документация для новой библиотеки отражения по-прежнему отсутствует, но выполняется. Это позволяет легко использовать scalac внутри 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)

это даже больше, когда мы хотим знать, как код Scala переводится внутри. Ранее Вэнь нужно ввести scala -Xprint:typer -e "List(1,2,3) map (_+1)" получить внутренне представление. Кроме того, некоторые небольшие улучшения нашли там путь к новой версии, например:

scala> :type Predef
scala.Predef.type

Scaladoc получит некоторые тип-иерархия графе (нажмите на тип-иерархия).

С помощью макросов теперь можно улучшить сообщения об ошибках отличным способом. Существует библиотека под названием expecty, который делает это:

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

есть инструмент, который позволяет найти библиотеки, размещенные на GitHub, называется ls.неявно.ly.

IDEs теперь имеют некоторую семантическую подсветку, чтобы показать, является ли член объектом/типом/методом / чем угодно. Функция семантического выделения ScalaIDE.

функция javap REPL-это только вызов собственного javap,поэтому это не очень богатый инструмент. Вы должны полностью квалифицировать название модуля:

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

некоторое время назад я написал резюме как код Scala компилируется байт-код, который предлагает много вещей, чтобы знать.

и лучшей: это все сделано за последние несколько месяцев!

Итак, как использовать все эти вещи внутри REPL? Ну, это невозможно ... ещё нет. ;)

но я могу сказать вам, что однажды у нас будет такой REPL. REPL, который показывает нам документацию, если мы хотим ее увидеть. REPL, который позволит нам общаться с ним (может быть, как lambdabot). REPL, который позволяет нам делать крутые вещи, мы все еще не могу представить. Я не знаю, когда это произойдет, но я знаю, что многое было сделано за последние годы, и я знаю, что еще больше будет сделано в следующие годы.


Javap работает, но вы указываете его на scala.Predef.List, который является type, а не class. Направьте его вместо scala.collection.immutable.List.

теперь по большей части достаточно просто ввести значение и посмотреть, какой тип результата. Используя :type может быть полезно иногда. Я нахожу это использование getClass - это очень плохой способ сделать это, хотя.

кроме того, вы иногда смешиваете типы и значения. Например, здесь вы ссылаетесь на объект :::

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

и здесь вы ссылаетесь на класс :::

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

объекты и классы-это не одно и то же, и, на самом деле, есть общий шаблон объектов и классов с тем же именем, с определенным именем для их отношений: компаньоны.

вместо dir, просто используйте вкладку завершение:

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

если вы войдете в режим питания, вы получите намного больше информации, но это вряд ли для начинающих. Выше показано типы, методы и сигнатуры методов. Javap будет декомпилировать материал, хотя это требует от вас хорошей обработки байт-кода.

там есть и другие вещи -- обязательно посмотрите вверх :help, и посмотреть, что доступно.

документы доступны только через API scaladoc. Держите его открытым в браузере и используйте его поиск возможность быстро находить классы и методы. Также обратите внимание, что, в отличие от Java, вам не нужно перемещаться по список наследования для получения описания метода.

и они прекрасно ищут символы. Я подозреваю, что вы не потратили много времени на scaladoc, потому что другие инструменты doc там просто не до этого. Javadoc приходит на ум - это ужасно просматривать пакеты и классы.

если у вас есть конкретные вопросы стиль переполнения стека, используйте Символ Гончая для поиска по символам.

использовать еженощно Scaladocs: они будут отличаться от любой версии, которую вы используете, но они всегда будут наиболее полными. Кроме того, прямо сейчас они намного лучше во многих отношениях: вы можете использовать вкладку для чередования кадров, с автоматической фокусировкой на полях поиска, вы можете использовать стрелки для навигации по левому кадру после фильтрации, и введите, чтобы выбранный элемент появился на правом кадре. У них есть список неявных методов и диаграммы классов.

я обошелся с гораздо менее мощным REPL и гораздо беднее Скаладок - они работают вместе. Конечно, я пропустил туловище (теперь голова), чтобы получить мои руки на tab-завершении.


отметим, что scala 2.11.8 новая вкладка-завершение в Scala REPL смогите облегчить тип исследование/открытие.

теперь он включает в себя:

  • завершение CamelCase:
    попробуйте:
    (l: List[Int]).rro TAB,
    он расширяется до:
    (l: List[Int]).reduceRightOption

  • найти членов, введя любую CamelCased часть имени:
    попробуйте:
    classOf[String].typ TAB, чтобы получить getAnnotationsByType, getComponentType и другие!--12-->

  • полные бобовые геттеры без ввода get:
    попробуйте:
    (d: java.util.Date).day TAB

  • пресс TAB дважды, чтобы увидеть подпись метода:
    попробуйте:
    List(1,2,3).part TAB,
    который завершается:
    List(1,2,3).partition;
    нажмите TAB еще раз для отображения:
    def partition(p: Int => Boolean): (List[Int], List[Int])


вам нужно передать полное имя класса в javap.

Сначала возьмите его с помощью classOf:

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

затем использовать javap (не работает с repl для меня:": javap недоступен на этой платформе.") Итак, пример из командной строки, в repl, я считаю, вам не нужно указывать classpath:

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

но я сомневаюсь, что это поможет вам. Вероятно, вы пытаетесь использовать методы, которые вы использовали в динамических языках. Я крайне редко использую repl в scala (в то время как использовать его часто в javascript). IDE и источники-это все.