Это требование в Scala введено
по причинам типобезопасности. Поскольку
при составлении mixin-классов в
другой класс копируются только различия,
могло бы случиться так, что некоторые
привнесенные члены ссылались бы
на унаследованные члены, отсутствующие
в новом контексте, порождая исключение
“метод не найден”.
Неоднозначности. В Scala каждый
класс наследуется только от одного
суперкласса, и получает члены класса
от нескольких других классов в процессе
составления mixin-класса. Представьте, например,
следующий подкласс класса Buffer, который
вводит метод sameElements, а также внутренне
используемый метод forall.class ComparableBuffer[T]
extends Buffer[T]
{
def forall(p: T => Boolean) : Boolean =
{
val it = elements;
var res = true;
while (res && it.hasNext)
res=p(it.next);
res
}
def sameElements(b: IterableBuffer[T]) : Boolean
=
forall(elem => b.exists(elem.equals));
}
Можно создать новый класс стека
MyStack, который содержит функциональность,
предоставляемую как IterableBuffer, так и ComparableBuffer,
используя оба класса как mixin-ы.class MyStack[T]
extends Stack[T]
with IterableBuffer[T]
with ComparableBuffer[T]; // Ошибка!
В Scala методы, определенные в
mixin-ах, либо представляют новые методы,
либо переопределяют соответствующие
методы суперкласса. Как показано в
предыдущем примере, может случиться
так, что два mixin-а определят один
и тот же метод. Классу MyStack неясно,
какой из методов forall использовать.
Такая неоднозначность порождает
ошибку времени компиляции, которая
должна быть явно устранена программистом.
Возможное решение – ввести новый
метод forall, который перенаправляет
вызовы нужной реализации. Следующий
код использует примитив super[C], позволяющий
ссылаться на конкретные определения
в mixin-классе C:class MyStack[T] extends Stack[T]
with IterableBuffer[T]
with ComparableBuffer[T]
{
override def forall(p: T => Boolean) =
super[IterableBuffer].forall(p);
}
Trait
Кроме неоднозначностей, есть
еще одна серьезная проблема множественного
наследования – дилемма бриллиантового
наследования, возникающая, если класс
является наследником двух классов,
разделяющих один суперкласс. Без
дальнейших ограничений эти суперклассы
в данном сценарии будут унаследованы
дважды. Это приведет к удвоению
состояния, инкапсулированного в данных
суперклассах, и выльется в серьезные
проблемы с непротиворечивостью.
Чтобы избежать этого, Scala позволяет
подмешивать класс в другой класс,
только если он не использовался ранее
в этом другом классе как суперкласс
или mixin. К сожалению, это правило
накладывает сильные ограничения,
исключая множество случаев, когда
двойное наследование от одного класса
не является проблемой – в частности,
для классов без инкапсулированного
состояния (в эту категорию попадают
интерфейсы Java). По этой причине в Scala
введено понятие trait-ов. Trait-ы –
это абстрактные классы, не инкапсулирующие
состояния ни в виде определений
переменных, ни в виде предоставления
конструкторов с параметрами. Однако,
в противоположность Java-интерфейсам, они
могут реализовать конкретные методы.
Поскольку trait-ы не инкапсулируют
состояния, двойное наследование от
них в Scala разрешено. Таким образом,
в иерархии суперклассов класса можно
использовать один trait несколько раз.
Декомпозиция
Объектно-ориентированная
декомпозиция
Программистам часто приходится
иметь дело со структурированными данными.
В объектно-ориентированном языке
структурированные данные обычно реализуются
как набор классов, представляющих
различные структурные конструкции.
При работе со структурированными данными
программист может полагаться только
на методы, предоставляемые этими
классами.
Модель
данных
Модель данных для XML в Scala
– это неизменяемое представление
упорядоченного неранжированного дерева.
В таком дереве у каждого узла
есть метка, последовательность дочерних
узлов и ассоциативный список
атрибутов и их значений. Все это
описано в trait-е scala.xml.Node, который, кроме
того, содержит эквиваленты XPath-операторов
child и descendant-or-self, записываемые как \ и \\.
Для элементов, текстовых узлов,
комментариев, инструкций по обработке
и ссылок на сущности существуют конкретные
подклассы.
Автономные
компоненты
Язык Scala как таковой не
предоставляет никаких примитивов
для параллельного программирования.
Вместо этого ядро языка создавалось
так, чтобы упростить создание библиотек,
предоставляющих различные модели
параллелизма, строящиеся поверх поточной
модели языка-основы. В этом разделе
будет продемонстрирована мощь Scala
на примере реализации небольшой
библиотеки для отказоустойчивых активных
объектов, подобных акторам Erlang (Erlang –
функциональный язык, предназначенный
для конкурентного (многопоточного)
программирования, разработанный фирмой
Ericsson, и используемый в телекоммуникационных
системах этой фирмы – прим.ред.).
Адаптация
компонентов
Каждая компонентная система
с мощными конструкциями абстракции
и композиции сталкивается с проблемой,
когда дело доходит до интеграции
подсистем, разработанных различными
командами в разное время. Проблема
состоит в том, что интерфейс
компонентов, разработанных той
или иной группой, часто не подходит
клиентам, намеренным использовать этот
компонент. Рассмотрим, например, библиотеку
с классом наподобие GenList, обсуждавшегося
выше. Клиент этой библиотеки может
захотеть рассматривать такие списки
как множества (sets), поддерживающие
операции включения членов или проверки
вхождения элемента во множество. Однако
поставщик класса не предполагал
такого сценария использования, и, следовательно,
не включил эти методы в интерфейс
класса GenList.
Виды
(views)
Scala представляет новую
концепцию решения проблемы внешней
расширяемости – виды (views). Они
позволяют расширять класс новыми
членами и trait-ами. Виды в
Scala переводят в объектно-ориентированное
представление используемые в
Haskell классы типов (type classes). В отличие
от классов типов, область видимости
видов можно контролировать, причем
в разных частях программы
могут сосуществовать параллельные
виды.
Какие виды доступны для
вставки? Scala рассматривает в качестве
кандидатов все виды, к которым
имеется доступ из точки вставки
без префиксного выражения. Это
включает как виды, определенные локально
или в некоторой области видимости,
так и виды, унаследованные от базовых
классов или импортированные
из других объектов выражением import. Локальный
вид не прячет виды, определенные в
прилегающей области видимости.
Вид применим, если он может быть
применен к выражению, и он позволяет
отобразить это выражение на желаемый
тип. Из всех кандидатов Scala выбирает наиболее
точно подходящий вид. В данном случае
точность интерпретируется так же,
как при разрешении перегрузки в
Java и Scala. Если применимого вида нет,
или среди применимых не удается
выбрать подходящий – генерируется
ошибка.
Локальность обеспечивается
тем ограничением, что в качестве
кандидатов рассматриваются только
те виды, которые доступны без префикса.
Клиенты могут приспосабливать
под свои нужды набор доступных
видов, выборочно импортируя объекты,
определяющие виды.
Виды часто используются
в библиотеке Scala, чтобы дать возможность
Java-типам поддерживать trait-ы Scala. Примером
может служить Scala-trait Ordered, определяющий
набор операций сравнения. Виды на этот
тип от всех базовых типов и
класса String определены в модуле scala.Predef.
Поскольку члены этого модуля
неявно импортируются в каждую Scala-программу,
эти виды всегда доступны. С точки
зрения пользователя, это похоже на
расширение Java-классов новыми trait-ами.
Границы
видов
До сих пор методы видов
должны были быть видимы статически в
точке вставки. Виды становятся еще
более полезными, если можно абстрагироваться
от конкретного вставляемого метода.
MultiJava – это консервативное
расширение Java, которое добавляет
симметричную множественную диспетчеризацию
и открытые классы. Оно предоставляет
альтернативное решение многих
проблем, которыми занимается
и Scala. Например, множественная диспетчеризация
дает решение проблемы бинарных
методов, которая в Scala решается
абстрактными типами. Открытые классы
предоставляют решение проблемы
внешней расширяемости, которая
в Scala решается с помощью видов.
Только в MultiJava встречается возможность
динамического добавления новых
методов в класс, так как
открытые классы интегрированы
с обычным процессом динамической
загрузки в Java. Напротив, только Scala
позволяет определять область
видимости внешних расширений
класса в программе.
Заключение
Scala – это одновременно
и большой, и относительно маленький
язык. Это большой язык в том
смысле, что он обладает богатыми
синтаксисом и системой типов,
комбинирующими концепции объектно-ориентированного
и функционального программирования.
Следовательно, пользователям, приходящим
из любого языкового сообщества,
придется изучать новые конструкции.
Большая часть разнообразия Scala порождена
желанием быть ближе к таким
традиционным языкам, как Java и
C# – чтобы облегчить принятие
Scala пользователями этих языков.
Scala – это также и
относительно маленький язык
в том смысле, что он построен
на скромном наборе весьма
общих концепций. Многие конструкции
являются синтаксическим сахаром,
который может быть удален
с помощью трансформации. Такие
обобщения, как унифицированная
объектная модель, позволяют абстрагироваться
от многих примитивных типов
и операций, перекладывая работу
с ними на плечи библиотеки
Scala. Спецификация и реализация Scala
также показывают, что ее сложность
управляема. Современный frontend компилятора
Scala имеет размер, сравнимый с
frontend-ом Sun Java 1.4. Текущая спецификация
Scala [9] (около 100 страниц) значительно меньше
текущей спецификации Java 1.4 [18] (около 400
страниц). Эти числа трудно сравнивать,
однако, поскольку спецификации Scala все
еще недостает зрелости спецификации
Java, и она использует краткие формулы во
многих местах, где спецификация Java использует
словесное описание.
Scala была выпущена для
общего пользования на платформе
JVM в январе 2004 года и на платформе
.NET в июне 2004 года. Реализация завершена,
за исключением runtime-типов, которые
должны появиться в конце 2004
года. В будущем мы собираемся
поэкспериментировать с улучшенной
систематической поддержкой типов
для XML, а также расширить набор
стандартных библиотек Scala.
Список
литературы
- Dean Wampler Alex Payne. Programming Scala: Scalability = Functional Programming
+ Objects,http://programming-scala.labs.oreilly.com/. O’Reilly, 2009.
- Derek Chen-Becker. Проект на Lift — <<Мелочь в Кармане>>. Проект,http://github.com/tjweir/pocketchangeapp/tree/master/PocketChange
- Marius Danciu David Pollak, Derek Chen-Becker and Tyler Weir. Starting
with Lift. Веб-страница,http://old.liftweb.net/docs/getting_started/mod_master.html.
- David Pollack et al. Getting Started With Lift. Веб-сайт, http://liftweb.net/getting_started.
- Debashish Ghosh. Designing Internal DSLs in Scala. Блог,http://debasishg.blogspot.com/2008/05/designing-internal-dsls-in-scala.html.
- Tyler Weir Marius Danciu, Derek Chen-Becker. The Definitive Guide to Lift: A Scala-based Web Framework,http://www.amazon.com/Definitive-Guide-Lift-Scala-based-Framework/dp/1430224215. Apress, 2007.
- Gregory Meredith. Pro Scala: Monadic Design Patterns for the Web, http://www.amazon.com/Pro-Scala-Monadic-Design-Patterns/dp/143022844X. Apress, 2010.
- JP Moresmaugh. Java and higher order generics. Блог, http://jpmoresmau.blogspot.com/2007/12/java-and-higher-order-generics.html.
- Tony Morris. Проект scalaz. Проект в Google Code, http://code.google.com/p/scalaz/.
- David Pollack. Beginning Scala, http://www.amazon.com/Beginning-Scala-David-Pollak/dp/1430219890. Apress, 2009.
- David Rupp. Java generics broken? we report, you
decide. Блог,http://davidrupp.blogspot.com/2008/01/java-generics-broken-we-report-you.html.
- Bill Venners. Scalatest. Проект, http://www.scalatest.org/.
- Bill Venners, Martin Odersky, and Lexi Spoon. Programming in Scala: A Comprehensive Step-by-step Guide,http://www.amazon.com/Programming-Scala-Comprehensive-Step---step/dp/0981531601/. Artima, 2008.
- Лекси Спун, Бил Веннерс, Мартин Одерски. Первые шаги в Scala. RSDN,http://www.rsdn.ru/article/scala/scala.xml.
- Мартин Одерски и другие. Обзор языка программирования Scala. RSDN,http://www.rsdn.ru/article/philosophy/Scala.xml.
- Антон Панасенко. Scala: Actors (part 2). Блог, http://blog.apanasenko.me/2009/12/scala-actors-part-2/.
- Антон Панасенко. Scala: Functional Language (part 3). Блог, http://blog.apanasenko.me/2009/12/scala-functional-language-part-3/.
- Антон Панасенко. Scala: введение в мир FL JVM (part 1). Блог, http://blog.apanasenko.me/2009/12/scala-fl-jvm-part-1/.