Почему в Scala API есть две стратегии организации типов? - PullRequest
4 голосов
/ 16 марта 2011

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

  1. Использование пакетов, членами которых они являются, импортированные. Так, например, вы получаете доступ к scala.collection.mutable.ListBuffer. Эта техника знакома по Java, Python и т. Д.

  2. Использование типов членов черт. Так, например, вы получаете доступ к типу Parser. Сначала нужно смешать в scala.util.parsing.combinator.Parsers. Эта методика не знакома для Java, Python и т. Д. И мало используется в сторонних библиотеках.

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

Ответы [ 2 ]

5 голосов
/ 17 марта 2011

Номенклатура примечания здесь зависимые от пути типы . Это вариант № 2, о котором вы говорите, и я буду говорить только об этом. Если вам не удалось решить проблему, вы всегда должны использовать вариант № 1.

Что вам не хватает, так это то, что класс Parser ссылается на вещи, определенные в классе Parsers. Фактически, сам класс Parser зависит от того, что input было определено в Parsers:

abstract class Parser[+T] extends (Input => ParseResult[T])

Тип Input определяется следующим образом:

type Input = Reader[Elem]

А Elem является абстрактным. Рассмотрим, например, RegexParsers и TokenParsers. Первый определяет Elem как Char, а второй определяет Token. Это означает, что Parser для каждого отличается. Что еще более важно, поскольку Parser является подклассом Parsers, компилятор Scala будет гарантировать, что во время компиляции вы не передаете RegexParsers Parser в TokenParsers или наоборот. На самом деле, вы даже не сможете передать Parser одного экземпляра RegexParsers другому его экземпляру.

4 голосов
/ 16 марта 2011

Второй также известен как Торт .Преимущество заключается в том, что код внутри класса, в котором есть смешанная черта, становится независимым от конкретной реализации методов и типов этой черты.Это позволяет использовать члены черты, не зная, какова их конкретная реализация.

trait Logging {
  def log(msg: String)
}

trait App extends Logging {
  log("My app started.")
}

Выше черта Logging является требованием для App (требования также могут быть выражены с помощью самоподтипов).Затем в какой-то момент в вашем приложении вы можете решить, какой будет реализация, и смешать черту реализации с конкретным классом.

trait ConsoleLogging extends Logging {
  def log(msg: String) = println(msg)
}

object MyApp extends App with ConsoleLogging

Это имеет преимущество перед импортом в том смысле, что требования вашегочасть кода не привязана к реализации, определенной оператором import.Кроме того, он позволяет создавать и распространять API, который можно использовать в другой сборке где-то еще, при условии, что его требования удовлетворяются путем смешивания в конкретной реализации.

Однако есть несколько вещей, которые следует соблюдатьс использованием этого шаблона.

  1. Все классы, определенные внутри черты, будут иметь ссылку на внешний класс.Это может быть проблемой, связанной с производительностью, или когда вы используете сериализацию (когда внешний класс не сериализуем, или хуже, если это так, но вы не хотите, чтобы он сериализовался).
  2. Если ваш «модуль» становится действительно большим, у вас будет очень большая черта и очень большой исходный файл, или вам придется распределить код черты модуля по нескольким файлам.Это может привести к некоторому шаблону.
  3. Это может заставить вас написать все приложение, используя эту парадигму.Прежде чем вы это узнаете, каждый класс должен будет смешать свои требования.
  4. Конкретная реализация должна быть известна во время компиляции, если вы не используете какое-то рукописное делегирование.Вы не можете динамически смешивать черту реализации, основываясь на значении, доступном во время выполнения.

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

...