Автор работы: Пользователь скрыл имя, 21 Мая 2013 в 21:29, курсовая работа
Язык Scala стал результатом исследований, направленных на разработку более хорошей языковой поддержки компонентного ПО. С помощью Scala я хотела бы проверить две гипотезы.
Введение 3
История 5
Истоки дизайна 5
Ключевые аспекты языка 5
Объектно-ориентированный язык 7
Функциональный язык 7
Java-подобный язык 7
Классы 9
Операции 11
Переменные и свойства 15
Операции – это объекты 15
Методы и функциональные значения 16
Функции – это объекты 17
Последовательности 18
For Comprehensions 18
Абстракции 19
Функциональная абстракция 20
Вариантность (Variance). 21
Абстрактные члены 23
Семейный полиморфизм (family polymorphism) и self-типы. 24
Моделирование обобщенных (generic) типов с помощью абстрактных типов 24
Композиция 27
Повторное использование классов 27
Наследование. 28
Trait 31
Декомпозиция 32
Объектно-ориентированная декомпозиция 32
Модель данных 32
Автономные компоненты 32
Адаптация компонентов 33
Виды (views) 33
Границы видов 34
Заключение 35
Список литературы 36
Оглавление
Введение 3
История 5
Истоки дизайна 5
Ключевые аспекты языка 5
Объектно-ориентированный язык
Функциональный язык 7
Java-подобный язык 7
Классы 9
Операции 11
Переменные и свойства 15
Операции – это объекты 15
Методы и функциональные значения 16
Функции – это объекты 17
Последовательности 18
For Comprehensions 18
Абстракции 19
Функциональная абстракция 20
Вариантность (Variance). 21
Абстрактные члены 23
Семейный полиморфизм (family polymorphism) и self-типы. 24
Моделирование обобщенных (generic) типов с помощью абстрактных типов 24
Композиция 27
Повторное использование классов 27
Наследование. 28
Trait 31
Декомпозиция 32
Объектно-ориентированная
Модель данных 32
Автономные компоненты 32
Адаптация компонентов 33
Виды (views) 33
Границы видов 34
Заключение 35
Список литературы 36
В настоящее время существует огромное количество языков программирования различных типов и уровней сложности. Один из них это язык объектно-ориентированного программирования Scala. С помощью языков программирования создаются компоненты программного обеспечения. В идеале, программное обеспечение должно собираться из библиотек предварительно написанных компонентов, так же, как аппаратура собирается из предварительно изготовленных чипов. На самом же деле большие куски приложений пишутся "с нуля", так что разработка ПО – это все еще больше ремесло, чем индустрия. Компоненты в этом смысле – простые части ПО, так или иначе используемые более крупными частями или целыми приложениями. Компоненты могут выглядеть по-разному. Это могут быть модули, классы, библиотеки, фреймворки, процессы или, например, Web-сервисы. Их размер может составлять от нескольких строк до нескольких тысяч. Они могут быть связаны с другими компонентами разнообразными механизмами – такими, как агрегирование, параметризация, наследование, удаленный вызов или передача сообщений.
Я считаю, что, по крайней мере, частично отсутствие прогресса в компонентном ПО объясняется недостатками языков программирования, используемых для определения и интеграции компонентов. Большинство существующих языков предлагает только ограниченную поддержку абстрагирования и композиции компонентов. Это относится, в частности, к таким статически типизированным языкам, как Java и C#, которые широко используются при создании компонентного ПО.
Язык Scala стал результатом исследований, направленных на разработку более хорошей языковой поддержки компонентного ПО. С помощью Scala я хотела бы проверить две гипотезы. Во-первых, я считаю, что язык программирования компонентного ПО должен быть масштабируемым в том смысле, что должна быть возможность с помощью одних и тех же концепций описать как маленькие, так и большие части. Поэтому сконцентрировалась на механизмах абстракции, композиции и декомпозиции вместо введения большого количества примитивов, которые могут быть полезными только на каком-то одном уровне масштабирования. Во-вторых, считаю, что масштабируемая поддержка компонентов может быть предоставлена языком программирования, унифицирующим и обобщающим объектно-ориентированное и функциональное программирование. Некоторые из основных технических новшеств Scala – это концепции, представляющие собой сплав этих парадигм программирования. В статически типизированных языках, к которым относится Scala, эти парадигмы до сих пор были почти полностью разделены.
Язык был создан в 2001—2004 годах в Лаборатории методов программирования EPFL. Scala была выпущена для общего пользования на платформе JVM в январе 2004 года и на платформе .NET в июне 2004 года. Планируется продолжить работу над формализацией ключевых аспектов языка и над разработкой оптимизаций, выполняемых компилятором.
На дизайн Scala оказали влияние многие языки и исследовательские работы. Следующее перечисление включает часть работ.
Scala впитала значительное
число концепций и
Scala-программы во многом похожи на Java-программы, и могут свободно взаимодействовать с Java-кодом.
Scala включает единообразную объектную модель — в том смысле, что любое значение является объектом, а любая операция — вызовом метода.
Scala — это также функциональный язык в том смысле, что функции — это полноправные значения.
В Scala включены мощные и единообразные концепции абстракций как для типов, так и для значений.
Она содержит гибкие симметричные конструкции примесей для композиции классов и trait-ов.
Она позволяет производить декомпозицию объектов путем сравнения с образцом.
Образцы и выражения были обобщены для поддержки естественной обработки XML-документов.
В целом, эти конструкции позволяют легко выражать самостоятельные компоненты, использующие библиотеки Scala, не пользуясь специальными языковыми конструкциями.
Листинг 1. Простая программа на Java и Scala.// Java
class PrintOptions
{
public static void main(String[] args)
{
System.out.println("Options selected:");
for (int i = 0; i < args.length; i++)
if (args[i].startsWith(""))
System.out.println(" " + args[i].substring(1));
}
}
// Scala
object PrintOptions
{
def main(args: Array[String]) : unit =
{
System.out.println("Options selected:");
for (val arg <- args)
if (arg.startsWith("-"))
System.out.println(" "+arg.substring(1));
}
}
Scala допускает внешние расширения компонентов с использованием видов (views).
На текущий момент Scala реализована на платформах Java и .NET.
В Scala используется чистая объектно-ориентированная модель, похожая на применяемую в Smalltalk: каждое значение — это объект, и каждая операция — это отправка сообщения. Например, сложение x+y интерпретируется как x.+(y), то есть как вызов метода + с аргументом x в качестве объекта-приёмника и y в качестве аргумента метода. Рассмотрим другой пример: 1+2. Это выражение интерпретируется как (1).+(2).Обратите внимание, что скобки вокруг чисел обязательны, потому что лексический анализатор Scala разбивает выражение на лексемы по принципу самого длинного возможного сопоставления. Таким образом, выражение 1.+(2) разобьется на лексемы 1.,+ и 2, потому что лексема 1. длиннее лексемы 1 и первый аргумент сложения будет интерпретирован, как тип Double вместо Int.[3]
Каждая функция — это
значение. Язык предоставляет легковесный
синтаксис для определения
Scala рассчитана на взаимодействие
с такими ведущими платформами,
Для простоты в дальнейшем мы будем сравнивать Scala только с Java. Но, поскольку Java и C#, в свою очередь, имеют много общего, сходство с Java распространяется и на C#. В чем-то Scala даже ближе к C#, чем к Java. Например, в трактовке обобщенности (genericity).
В листинге 1 приведен пример простой программы на Java и Scala. Программа распечатывает все опции, введенные в командной строке. Примеры очень похожи. Оба языка используют один и тот же класс-примитив String, вызывающий одни и те же методы. Они также используют одинаковые операторы и одинаковые условные управляющие конструкции. Пример показывает и некоторые различия между языками:
В Scala кроме определений классов есть определения объектов (начинающиеся с object). Определения объектов определяют класс с одним экземпляром – который иногда называют singleton-объектом. В приведенном примере singleton-объект PrintOptions содержит функцию-член main. Singleton-объекты в Scala заменяют статические части Java-классов.
Scala использует для определений
и параметров синтаксис id:
Синтаксис Scala более систематичен, чем Java – все определения начинаются с ключевого слова. В приведенном примере с def main начинается определение метода.
В Scala нет специального синтаксиса для типов массивов и доступа к ним. Массив с элементами типа T записывается как Array[T]. Здесь Array – это стандартный класс, а [T] – параметр типа. На самом деле, массивы в Scala наследуются от функций. Поэтому обращение к массиву выглядит как вызов функции: a(i), вместо a[i] в Java.
Возвращаемый main тип выглядит
как unit вместо используемого в Java void.
Это следствие того, что в Scala предложения
(statement) и выражения (expression) – это
одно и то же. Каждая функция возвращает
значение. Если правая часть функции
– это блок, то будет возвращено
значение последнего выражения. Результат
может иметь тривиальное
В Scala используется большинство управляющих структур Java, но традиционное для Java выражение for в их число не входит. Вместо этого существует for-comprehension, который дает возможность прямого перебора элементов массива (или списка, или перечисления) без необходимости в индексации. В Java 1.5 тоже есть понятие "расширенного цикла for", похожее на for-comprehension Scala, но имеющее больше ограничений.
Несмотря на различия в синтаксисе, Scala-программы могут без проблем взаимодействовать с Java-программами. В приведенном примере, Scala-программа вызывает методы startsWith и substring класса String, определенного на Java. В ней также происходит обращение к статическому полю out Java-класса System и вызов его (перегруженного) метода println. Это возможно даже несмотря на то, что в Scala нет концепции статических членов класса. На самом деле каждый Java-класс представляется в Scala как две сущности – класс, содержащий все динамические члены, и singleton-объект, содержащий все статические члены. Таким образом, System.out в Scala доступен как член объекта System.
Хотя это и не показано
в приведенном выше примере, классы
и объекты в Scala могут наследовать
от Java-классов и реализовать Java-интерфейсы.
Это позволяет использовать Scala-код
во фреймворке Java. Например, Scala-класс
мог бы реализовывать интерфейс
java.util.EventListener. Экземпляры этого класса
затем можно оповещать о
Каждый класс Scala унаследован
от класса Scala.Any. Подклассы Any попадают
в одну из двух категорий: классы-значения,
наследуемые от scala.AnyVal, и ссылочные
классы, наследуемые от scala.AnyRef. Любое
имя примитивного Java-типа соответствует
классу-значению, и отображается на
него с помощью предварительно определенного
псевдонима типа. В Java AnyRef отождествляется
с корневым классом java.lang.Object. Экземпляр
ссылочного класса обычно реализуется
как указатель на объект, хранящийся
в куче программы. Экземпляр класса-значения
обычно представляется напрямую, без
указателей-посредников. Иногда приходится
конвертировать два представления,
например, когда экземпляр класса-
Обратите внимание, что
классы-значения являются плоскими (одноуровневыми);
все классы-значения – это подтипы
scala.AnyVal, но не подтипы друг друга. Вместо
этого используются представления
(т.е. стандартные приведения) элементов
различных классов-значений. Здесь рассматривали
альтернативу в виде создания подтипов
классов-значений. Например, можно было
бы сделать Int подтипом Float, вместо создания
стандартного приведения от Int к Float. Праграммисты
отказались от этого, поскольку хотели
сохранить как инвариант то, что интерпретация
значения подкласса как экземпляра его
суперкласса не меняет представления
значения. Кроме всего прочего, они хотят
гарантировать, что для каждой пары типов
S<:T и каждого экземпляра x типа S выполнено
следующее равенство:x.asInstanceOf[T].
Внизу иерархии типов лежат два класса – scala.AllRef и scala.All. Тип AllRef – это подтип всех ссылочных типов; его единственный экземпляр – ссылка на null. Поскольку AllRef не является подтипом типов-значений, null не является членом любого такого типа. Например, невозможно присвоить null переменной типа int.
Тип All – это подтип любого другого типа; экземпляров этого типа не бывает. Несмотря на то, что тип All – пустой, он может быть полезен в качестве параметра типа. Например, в библиотеке Scala определено значение Nil типа List[All]. Поскольку списки в Scala ковариантны, это делает Nil экземпляром List[T] при любом типе элементов T.
Операция равенства значений (==) разрабатывалась так, чтобы быть прозрачной в отношении представления типов. Для типов-значений это обычное (числовое или булево) равенство. Для ссылочных типов == расценивается как псевдоним метода equals из java.lang.Object. Этот метод изначально определяет равенство как равенство ссылок и должен переопределяться в подклассах, чтобы реализовать естественное понятие равенства для этих подклассов. Например, boxed-версии типов-значений могли бы реализовать метод equals как сравнение boxed-значений. В Java, наоборот, == всегда означает равенство ссылок для ссылочных типов. Это реализуется несколько эффективнее, но создает проблемы связности, поскольку boxed-версии равных значений могут оказаться неравными (относительно ==).
В некоторых ситуациях
нужно не определяемое пользователем,
а ссылочное равенство. Примером
может быть хеш-консолидация, где
эффективность – это все. Для
таких случаев класс AnyRef определяет
дополнительный не переопределяемый метод
eq, реализующий ссылочное
Еще один аспект унифицированной объектной модели Scala – каждая операция является отправкой сообщения, то есть, вызовом метода. Например, сложение x+y интерпретируется как x.+(y), т.е. как вызов метода + с x в качестве объекта-приемника и y в качестве аргумента метода. Эта идея, впервые реализованная в Smalltalk, адаптирована к более традиционному синтаксису Scala следующим образом. Во-первых, Scala рассматривает имена операторов как обычные идентификаторы. Точнее, идентификатор – это либо последовательность букв и цифр, начинающаяся с буквы, либо последовательность операторных символов. Таким образом, можно определять, например, методы с именами +, <= или :: Далее, Scala трактует идентификатор, находящийся между двумя выражениями, как вызов метода. Например, в листинге 1 можно было бы использовать синтаксис операторов (arg startsWith"-") как "синтаксический сахар" для более традиционного синтаксиса (arg.startsWith("-")).