Как исследовать объекты/типы/и т.д. от 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 вы можете попробовать и прочитать документацию в Интернете, найти исходный код в библиотеке и т.д., но это часто может быть очень сложно для вещей, где вы не знаете, где и даже что они (и часто это большой кусок, чтобы откусить, учитывая иерархию объемного типа) - материал разворачивается в разных местах (пакет scala, Predef, различные неявные преобразования, символы типа ::, которые почти невозможны гуглить). REPL должен быть способом непосредственного изучения, но на самом деле вещи гораздо более загадочны. Скажите, что я где-то видел ссылку на foo, но я понятия не имею, что это такое. По-видимому, нет такой вещи, как "руководство по систематическому исследованию" w60 > штучек с REPL ", но следующее, что я собрал вместе после большого количества проб и ошибок:

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

Пример отказа с использованием :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 = [email protected]

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

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

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

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

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

Ответ 1

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

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

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

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

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

Большие улучшения приходят с Scala2.10 в виде собственной библиотеки Reflection 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)

Документация для новой библиотеки Reflection все еще отсутствует, но выполняется. Это позволяет использовать скаляр в простой форме внутри 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] = [email protected]

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)

Это еще больше, когда мы хотим знать, как код 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.implicit.ly.

Теперь у IDE есть семантическая подсветка, чтобы показать, является ли элемент объектом/типом/методом/независимо. Функция смысловой подсветки 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 скомпилирован в Bytecode, который предлагает много вещей, чтобы знать.

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

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

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

Ответ 2

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 приходит на ум - это ужасный просмотр пакетов и классов.

Если у вас есть конкретные вопросы Qaru style, используйте Symbol Hound для поиска с символами.

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

Я поступил с гораздо менее мощным REPL и гораздо более бедным Scaladoc - они работают вместе. Конечно, я проскользнул в багажник (теперь HEAD), чтобы получить доступ к табуляции.

Ответ 3

Обратите внимание, что scala 2.11.8 Новое заполнение вкладок в Scala REPL может облегчить поиск/обнаружение типа.

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

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

  • Найти участников, набрав часть имени CamelCased:
    попробовать:
    classOf[String].typ TAB для получения getAnnotationsByType, getComponentType и других

  • Завершить bean геттеры без ввода:
    попробовать:
    (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])

Ответ 4

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

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

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

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

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

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