Pull to refresh

OMG Scala is a complex language!

Reading time 5 min
Views 19K
Original author: Razvan Cojocaru
Я постоянно замечаю, что «О, святые небеса, Scala — это сложно!». Знаете, возможно это правда. Давайте попробуем посмотреть на эту сложность и пройтись по основным отличиям Scala от других объектно-ориентированных языков (например, Java или C++).



Вывод типов


    В большинстве случаев, компилятор способен вывести тип, что позволяет разработчикам писать меньше повторяющейся информации, которую они так привыкли писать в Java и C++.
    К примеру, совершенно ясно, что «john» это строка, так что компилятор может вывести тип самостоятельно, и мне не придется указывать тип явно. Я могу просто написать:

  val aGuy = "John"

    Я встречал как одобрение так и критику, касающуюся вывода типов, так что даже и не знаю, что сказать. Лично мне нравится писать меньше и чувствовать себя чуть менее глупым всякий раз, когда я объявляю значение или переменную.

Упрощенное определение классов


    Так как OOP строится на основе определения классов, то в Scala это реализовано максимально просто. Все мои доменные модели получаются предельно простыми.

class Person (val firstName:String, val lastName:String)

    Надо сказать, что тоже самое в Java или C++ заняло бы почти страницу кода со всякими там конструкторами, геттерами, сеттерами и т.д. Разработчики Scala поняли, что люди не должны писать целую страницу кода лишь ради того, чтобы сообщить глупому компилятору, что им нужен класс Person, у которого есть два поля: имя и фамилия. Так что, в Scala, определение такого класса занимает всего лишь одну строчку, так же как выглядела бы таблица в SQL.
    Добавляет ли это сложности?
    Ну… Мне нужно обеспокоится разве что в тех случаях, когда мне нужно переопределить сгенерированные геттеры и сеттеры. Так что я даже и не знаю, действительно ли это усложняет жизнь…
    Я?! Лично я настолько в восторге от этой возможности, что, честно говоря, мне абсолютно всё равно, что вы там себе думаете.

Объединение методов и операторов


    Все языки программирования, которые я знаю, проводят жесткую грань между методами с именем, типа «append», и операциями, типа "+=". Некоторые языки, запрещают переопределение существующих операторов (Java). Другия языки разрешают только инфиксную нотацию для операторов (C++). В Scala нет всех этих ограничений. Вы можете использовать любые имена для названия методов, а инфиксная нотация разрешена для любых вызовов:

ListBuffer(1,2,3) append 4

// И так тоже можно 

ListBuffer(1,2,3) += 4

    Единственное отличие будет состоять в правилах приоритетности. Конечно, кто-то скажет, что это «более сложно», чем в Java или C++, потому что это «развязывает руки» и позволяет делать всё что хочешь. Однако, я считаю, что это более просто, чем в упомянутых языках. В конце концов, это делает Scala совершенным фреймворком для создания естественного DSL:

"Mary" with "a purse" and "red shoes" should look "nice"


Типы


    И в Java и в C++ шаблоны имеют определенное зашитое ограниченное поведение (т.е. ковариантность) и позволяет только несколько конструкций (наподобие List<T extends Person>).
В Scala есть дефолтное поведение, при котором List[Person] невариантен, но всё можно настроить. Если вам нужна ковариантность, просто скажите компилятору List[+Person] или List[-Person] для контрвариации. На подобии Java, есть конструкция List[T <: Person] и List[T >: Person]. Так как в Scala есть механизм неявных преобразований (implicits), то также доступна еще одна конструкция, как раз для таких случаев: List[T <% Person].
    Сложнее ли это?
    Так же как и с операторами, этот механизм порождает некоторые ограничения, однако, с другой стороны предоставляет новые возможности. Лично мне нравится этот контроль типов. Пользовался ли я этим? Не в каждодневной разработке, чаще всего хватает обычного дефолтного поведения контроля типов.

Только конструкторы?


    Большинство языков позволяет только конструировать объекты. Scala позволяет также деконструировать объекты, при этом я не говорю об освобождении памяти, занимаемой объектом. Рассмотрим:

someone match {
  case Person(first,last) => println (s" name is $first $last")
}

    Здесь вы можете увидеть, что я подразумеваю под «деконструкцией» объекта. Возьмем уже созданный объект и деконструируем его на компоненты first и last. Я знаю, это выглядит чуждно и непривычно для большинства сторонников OO, но поверьте мне, это безумно круто и удобно! Представьте, что вам пришлось бы сделать, для того чтобы реализовать подобное в Java или C++… Проверить на тип (instanceof), присвоить две переменные на эти значения и еще много чего…
    Вспомните про интерполяцию строк в Scala:

"$first $last"


    Это более сложно? Ну… Это совершенно новый функционал. Мне нравится! Поверьте, вам тоже это понравится!
    Вот, кстати, match/case предоставляет более широкий спектр возможностей, нежели обычный switch/case, который умеет работать только с константами. С помощью match/case можно деконструировать объекты, матчить константы, матчить типы, и многое, многое другое! Да посмотрите сами:

someone match {

  case Person(first,last) => println (" name is " + first + " " + last)

  case "John" | "Mary" => println ("hello, " + someone.toString)

  case s:String => println ("name is " + s)

  case _ => println ("don’t know what this is…")

}

    Это сложно? Я не знаю… в Java или C++ это заняло бы от одной до двух страниц кода. Лично для меня, в Scala это выглядит просто и интутивно понятно.

Заключение


 Конечно, это лишь некоторые аспекты языка. Если вам есть что добавить, пишите пост, буду рад.
Я не касался функционального программирования на Scala, для этого нужно сравнивать Scala с другими функциональными языками, он я не FP'шник (FP guy). С++ сейчас позволяет передавать указатели на функции в другие функции, а в Java 8 введены lambda конструкции.

Сложно ли всё это? Ну… Можно посмотреть с двух сторон на всё это:
Да,
  • Больше символов и возможностей, которые можно использовать
  • Нужно учить некоторые дополнительные термины и понятия из области компьютерных наук (контравариация, паттерн матчинг, лямбды, замыкания и т.д.)

Нет,
  • Реальные проблемы решить можно
  • Такие же конструкции на Java или C++ сделать впринципе невозможно или это займет гораздо больше кода… и… код будет выглядеть ужасно.

    Что я думаю? Мне реально всё равно. Лично я очень рад, что познакомился с этими возможностями. Это позволяет мне решать проблемы всего лишь в несколько строчек кода, в то время как мои друзья всё еще пишут эти геттеры и сеттеры…

Негатив


    Есть всё же несколько вещей, которые мне не понравились, за чуть более чем шести летний опыт работы со Scala. Вот некоторые из них:
  1. Медленная компиляция. Не то чтобы раздражающе медленная, но всё же гораздо медленнее, чем у остальных.
  2. Неявные преобразования. Мне не нравится как они реализовали этот механизм. Можно случайно заимпортировать преобразование из Any в MyCoolThing, котороый был определен в каком-нибудь импортируемом блоке.
  3. Библиотека коллекций. Для большинства людей это не так важно, но… Всё это выглядит гораздо сложнее, чем мне хотелось бы.
  4. SBT. Это всегда сложно. Серьезно, если вам нужно сделать что-то выходящее за рамки типичной работы с SBT, то вы попали. Существует столько разных не очевидных DSL, что Google будет сходить с ума пытаясь найти для вас как нужно сделать то, что вам нужно. Им нужно было сделать либо какой-то отдельный синтаксис, наподобие JSON или какой-то свой внутренний DSL, который компилятор / IDE мог бы понимать и помогать при работе с SBT.


P.S. Более детальная дискуссия насчет сложности: Simple or Complicated?
Tags:
Hubs:
+13
Comments 88
Comments Comments 88

Articles