Смущает пример опции "Scala in Depth" - PullRequest
2 голосов
/ 20 марта 2012

Глава 2 новой книги Мэннинга "Scala in Depth" Джоша Суреша опубликована здесь . Читая статью, я наткнулся на этот фрагмент кода:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = {

   tmpArg.map(name => new java.io.File(name)).

          filter(_.isDirectory).

       getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))

}

Следующий текст, объясняющий код выше, гласил:

Метод getTevenDirectory принимает параметр командной строки как Опция, содержащая String и возвращающая ссылку на объект File временный каталог, который мы должны использовать. Первое, что мы делаем, это используем метод карты в Option для создания файла java.io.File, если был параметр. Далее мы убедимся, что этот вновь созданный объект файла это каталог. Для этого мы используем метод фильтра. Это проверит соответствует ли значение в Option некоторому предикату и, если нет, преобразовать в None. Наконец, мы проверяем, есть ли у нас значение в Вариант; в противном случае мы возвращаем временный каталог по умолчанию.

Итак, для меня, пришедшего с Java и изучающего Scala, синтаксис кода смущает меня. Я не понимаю, как существует точка после вызова функции map (...). Похоже, что происходит так много выводов типов, я чего-то упускаю и не вижу типов.

Мне было бы очень полезно, изучая Scala, иметь возможность каким-то образом увидеть все предполагаемые типы, отменить (или отменить) все сокращения, то есть чрезмерно подробную версию, похожую на pre-Java 6, где типы должны быть явными по обе стороны от равных для классов коллекции.

Есть ли где-нибудь инструмент, который бы брал фрагмент кода Scala и делал явные разные вещи (возможно, в виде флагов; один для типов, другой для имплицитов, другой для фигурных скобок, другой для точек с запятой). Мне просто нужно что-то, чтобы перейти от полностью сжатого кода к чему-то более близкому к Java, чтобы я мог чувствовать себя уверенно, развивая свои навыки чтения (и в конечном итоге написания) более лаконичного Scala.

Вот то, что я ищу:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = {

   ContainerType1[Type1] t1 = tmpArg.map(name => new java.io.File(name));
   ContainerType2[Type2] t2 = t1.filter(_.isDirectory);
   return t2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")));

}

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

Ответы [ 4 ]

11 голосов
/ 20 марта 2012

Что ж, у вас есть REPL, чтобы попробовать цепочечные команды одну за другой и проверить их типы результатов, но я не уверен, что подписи помогут вам так:

scala> Some("c:\\users\\paolo")
res0: Some[java.lang.String] = Some(c:\users\paolo)

scala> res0.map(name => new java.io.File(name))
res1: Option[java.io.File] = Some(c:\users\paolo)

scala> res1.filter(_.isDirectory)
res2: Option[java.io.File] = Some(c:\users\paolo)

scala> res2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))
res3: java.io.File = c:\users\paolo

Теперь давайте попробуем это снова, начиная с None.

scala> None:Option[String]
res6: Option[String] = None

scala> res6.map(name => new java.io.File(name))
res7: Option[java.io.File] = None

scala> res7.filter(_.isDirectory)
res8: Option[java.io.File] = None

scala> res8.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))
res9: java.io.File = C:\Users\paolo\AppData\Local\Temp

Таким образом, использование Option помогло нам "распространить" None без проверки на нулевые значения на каждом шаге, как мы, вероятно, сделали бы в Java.

Как вы видите, здесь не так много выводов типов. Я думаю, что корень вашей путаницы может заключаться в том, что map и filter (среди прочих) обычно связаны с коллекциями какого-либо рода, поэтому может быть трудно понять, что они делают в Option, которая лишь отдаленно похожа на коллекция. Для этого я отсылаю вас к классической scala.Option шпаргалке

2 голосов
/ 20 марта 2012

Конечная точка просто прикована к методу filter на следующей строке.В игре нет логического вывода.

Это можно переписать как

import java.io._

def getTemporaryDirectory(tmpArg : Option[String]) : File = {
    tmpArg.map(name => new File(name)).filter(_.isDirectory).getOrElse(new File(System.getProperty("java.io.tmpdir")))
}
1 голос
/ 20 марта 2012

Дополнительным ресурсом, наряду с REPL, является использование вашей IDE для декомпозиции и / или аннотирования связанных выражений.

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

Однако, повторяя совет Даниэля, такая книга, как «Программирование в Scala», была бы гораздо более простой отправной точкой, охватывающей вывод типов, а также правила синтаксиса языка (которые могли бы еще больше способствовать путанице в этом примере).

1 голос
/ 20 марта 2012

В явном виде:

def getTemporaryDirectory(tmpArg : Option[String]) : java.io.File = {
  val v1: Option[java.io.File] = tmpArg.map(name => new java.io.File(name))
  val v2: Option[java.io.File] = v1.filter(_.isDirectory)
  val v3: File = v2.getOrElse(new java.io.File(System.getProperty("java.io.tmpdir")))
}

Здесь все еще происходит много выводов типов, и у Даниэля Спивака отличная презентация по выводу типов . Это действительно дает вам представление о том, какие типы Scala способен выводить.

Более конкретно на этом примере.

Option[A] s map метод имеет подпись map[B](f: A => B): Option[B]. Поскольку tmpArg равно Option[String], компилятор знает, что тип параметра равен String => B. Теперь можно сделать вывод, что name есть String. Осматривая функцию, можно увидеть, что функция возвращает File. Теперь компилятор может сделать вывод, что тип параметра String => File, и что этот вызов map возвращает Option[File].

Option[A] s filter метод имеет подпись filter (p: A => Boolean): Option[A]. Функция литерал _.isDirectory - это просто сокращение для x => x.isDirectory. Учитывая, что A теперь File, компилятор может сделать вывод, что _ также File. И результат Option[File].

Наконец, у нас есть метод Option[A] getOrElse с подписью getOrElse[B >: A](default: => B): B. Синтаксис B >: A указывает параметр типа B, ограниченный таким же или супертипом, что и A. Синтаксис => B определяет параметр как параметр по имени / ленивости, который оценивается только при необходимости. Переданный параметр имеет тип File, что означает, что B равно File, и это getOrElse возвращает File.

...