Копипаста:Лурк:Участник:Orionll/Scala
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 — это ООП + ФП. То есть можно писать и ООП, можно и ФП. А некоторые скалолазы умудряются даже неплохо совмещать.
ООП в Скале отличается от ООП в жабе/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
(неявно).
Где используется[править]
Ви таки будете смеяться, но — в банках. То есть там же, где и сракле.