Участник:Orionll/Scala

Материал из Lurkmore
Перейти к навигации Перейти к поиску


Мартин

Scala (Скала, скакалка) — хипстофрикоэнтерпрайзный мультипарадигменный язык программирования со статической типизацией, продукт деятельности немецкого профессора Мартина Одерски. Позиционируется как замена языка Java™©®, что вызывает глубокое неудовольствие у сторонников как Java, так и Кложуры, другого перспективного языка программирования под JVM.

История

В начале 2000-х Одерски работал над языком Pizza, который был создан как площадка для экспериментов над языком Java. В Pizza Одерски сделал параметрический полиморфизм, функции-объекты первого класса, паттерн-матчинг и прочую функциональщину. Однако только дженерики в итоге перекочевали в Джаву, а от остального отказались. Дабы не пропадать добру, Одерски решил взять свои наработки и создал в 2003 году первый компилятор языка Scala, который как и Java, компилируется в байт-код JVM.

Java++

По идее, язык Scala задумывался таким образом, чтобы на нём могли писать абсолютно все, начиная от нубов-хеллоуворлдщиков и заканчивая продвинутыми девелоперами-задротами, в конец упоротыми такими вещами как теоркат, функторы и монады (в отличие от Haskell, который способны осилить только последние). К несчастью, Скала не защищает от возможности писать гавнокод на ней, и жабакодеры, пересаживающиеся на Скалу, продолжают писать в своём привычном джава-стайле. Поэтому в команде должен быть хотя бы один человек, который хорошо знает Скалу и который давал бы по рукам всем остальным.

Ходят слухи [1], что Scala для Java есть ни что иное как C++ для C. Но никто (даже труп страуса) в совершенстве не знает C++. Каждая C++ команда изобретает свой C++ диалект и презирает другие команды, которые пишут на «другом C++». Такая же участь может постигнуть и Scala. Однако есть и противоположное мнение [2], что «в отличие от C++, Scala становится тем проще, чем дальше её изучаешь».

В любом случае, факт остается фактом, что одну и ту же задачу на Scala можно решить многими способами, даже если это простой «Hello World», и этот аргумент явно не в пользу Scala.

ООП + ФП

Ниша Scala

Scala — это ООП + ФП. То есть можно писать и ООП, можно и ФП. А некоторые скалолазы умудряются даже неплохо совмещать.

ООП в Скале отличается от ООП в жабе/C#: в Скале есть множественное наследование. Но множественное наследование это правильное, не как в плюсах — реализуется через trait'ы (они же примеси — интерфейсы, у которых можно реализовывать методы), причем важен порядок наследования от примесей. Гуглить на тему stackable modifications, если интересно.

ФП в Скале также отличается от ФП в ML, Haskell, F# и других чисто функциональных (без ООП) языках со статической типизацией. Главное отличие — отсутствие type union'ов (которое, хотя, легко имитируется через наследование и case-классы). Второе — это отсутствие полного вывода типов, из-за чего программы на Scala получаются зачастую более громоздкими, чем например в Хаскеле, потому что часто приходится явно указывать типы параметризованных классов.

Фичи языка

  • Функции ничем не хуже, чем другие типы данных. Функции могут быть объявлены внутри других функций, могут возвращаться из функций и приниматься в качестве их аргументов.
  • Вменяемые обобщенные типы

В отличие от приплюснутых непроверяемых препроцессором шаблонов и стираемых во имя бобра и энтерпрайза джавовских дженериков, в Scala таки есть обобщенные классы и методы типы с контролем типов. Самый простой случай Java-style

class Tree[Type] {
    var node: Type = _
    var left : Tree[Type] = _
    var right : Tree[Type] = _
}

А вот класс Tree, который принимает в качестве параметра типа только числовые типы, то есть

любой из числовых типов Scala и их потомков

class NumericTree[Type <% AnyVal] {
    var node : Type = _
    var left : Tree[Type] = _
    var right : Tree[Type] = _
}

. Соответсвенно, можно указать и ограничение на типы предков и даже оба ограничения: и на предков и на потомков параметра обобщённого типа.

  • case-классы и паттерн-матчинг:
sealed trait Tree
case class Leaf(val v: Int) extends Tree
case class Node(val left: Tree, val right: Tree) extends Tree

/** Посчитать сумму дерева */
def sum(tree: Tree): Int = tree match {
  case Leaf(v) => v
  case Node(left, right) => sum(left) + sum(right)
}

Аналогичный жабакод занял бы в 10 раз больше места.

Признак sealed означает, что иерархия закрыта и никакой анонимус не сможет отнаследоваться от trait'а Tree. Оператор case означает, что Scala-компайлер нагенерит для классов Leaf и Node кучу шаблонных методов типа equals, hashCode (чтобы сравнивать) и apply (чтобы не писать каждый раз new).

Особенно крутая вещь — можно матчиться по вложенным паттернам (например, Node(Leaf(5), Leaf(v))).

  • object — встроенный в язык правильный синглтон (без всей этой вашей double checked locking и другой громоздкой хуиты)
  • lazy — инициализация происходит в момент первого использования, что в некоторых задачах экономит память
  • for-компрехеншены. Например, найти названия всех книг, среди авторов которых есть тот, чья фамилия начинается со слова Gosling:
for {
  b <- books;
  a <- b.authors
  if a startsWith "Gosling"
} yield b.title
  • В Scala есть implicit'ы, с помощью которых можно неявно привести что угодно к чему угодно. При чрезмерном использовании делает код абсолютно нечитаемым.
  • Могучая иерархия коллекций с кучей методов. В одном только интерфейсе Iterable over 104 метода. Пример использования:

Создаем список из чисел от 1 до 20 и делим его на два списка: с чётными и нечётными элементами

val (odd, even) = (1 to 20).toList.partition(f => f % 2 == 0)

, а затем считаем их суммы: можно так

val sumEven = even.fold(0)(_ + _)
val sumOdd = odd.fold(0)(_ + _)

, а можно и так

val sumEven = even.sum;
val sumOdd = odd.sum;

Анонимусу может быть интересно, каким образом работает odd.sum. Ответ прост: есть implicit из Int в специальный класс Numeric, а метод sum принимает на вход этот самый Numeric (неявно).

Где используется

Ви таки будете смеяться, но — в банках. То есть там же, где и сракле.

Мемы