Как я Scala учил

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

Но больше дискомфорта приносило даже не то, что я чего-то не понимал, а то что там многое по-другому, да даже тип переменной идет после названия, а порой его вообще нет.

final String str = "abc"; //Java

val str = "abc" // Scala

Вот так описывается функция:

int sum(int a, int b) {return a+b;} // Java

def sum(a: Int, b: Int) = {a + b} // Scala

А еще у Scala есть консоль REPL(Read-eval-print-loop), как, например, в Python. Как вы уже заметили пропали точки с запятой. Можно запускать одностраничные программы без main'а, названия методов и переменных могут содержать и начинаться вообще с любых символов, никаких правил. Там не static, но есть Object, там примитивы тоже объекты, == там на самом деле equals. Если у метода нет параметров то не обязательно ставить точку для вызова метода, скобки тоже опциональны если нет параметров, а если принимает только 1 параметр, то можно написать вот так:

str.charAt(5); // Java

str charAt 5 // Scala

И еще один интересный пример:

val res = 1 + 1

Нет, это не просто 1 плюс 1, здесь у объекта 1, вызывается метод + и ему передается единственным параметром объект 1. Для меня этого было разрывом шаблона.

На помощь моему шоку пришла замечательная книга Дэвида Поллака — Beginning Scala. Книга начинается с одной фразы, после которой я понял что обязательно должен дочитать ее до конца:
Ouch! That hurts my brain! Stop making me think differently. Oh, wait… it hurts less now. I get it. This different way of solving the problem has some benefits. I felt that way after my first year of law school. I felt that way for a while when I began coding Scala.
Дэвид имеет колоссальный опыт в программировании, начинал он это дело еще за 20 лет до моего рождения, успел поработать с более чем 10 языками программирования, пока не пришел к Scala. И теперь он говорит:
Scala is a programming language that provides a best-of-all-worlds experience for developers.
Тем не менее автор честно предупреждает, что овладеть им не так уж просто, ему для этого потребовалось 2 года, но он надеется, что мы сможем быстрее и он в этом поможет. Это не очень-то простая книга и она предполагает определенный опыт в программировании у читателя. Особенно она понравится тем, кто раньше программировал на Java, т.к. встречается много отсылок к этому языку и можно сравнивать.

Помимо Beginning Scala параллельно я читал уроки Twitter’s Scala School, для всех уроков есть перевод на русский язык, книгу Дэвида Поллака же удалось найти только в английском варианте.

Закрепить теорию помимо самостоятельных путешествий по исходникам скалы и работы помогал проект Scala Exercises, там очень простые задания, но вполне подходят для закрепления какого-то аспекта на первых порах и проверки того, что ты все внимательно прочитал и понял действительно правильно.

И расскажу немного про самые распространенные и очень простые вещи, которые пришлось постичь в первую очередь.

Option. Такого в Java нет. В двух словах — это контейнер в котором либо пусто(None, похоже на null, но имеет методы map, filter, ...), либо лежит какое-то ровно одно значение Some(value) и его использование может сделать код более безопасным и не кидающим NullPointerException, потому что хочешь не хочешь, а чистые данные из Option нужно еще извлечь, и вот в этот момент забыть написать проверку уже сложно.
Конечно, у Option есть метод get, но использовать его не рекомендуется потому что в этом случае теряется весь смысл Option, т.к. None.get вызывает исключение.
Несколько самых очевидных удобств:

Легко возвращать значение по умолчанию в случае пустого Option

optValue.getOrElse(defaultValue)

В случае каких-то действий:

optValue match {
      case Some(value) => action
      case None => defaultAction
}

Пример из Scala Beginning. Путь есть некая база данных, которая содержит записи типы Person

def findPerson(key: Int): Option[Person]

Метод вернет Some[Person] если такая запись будет найдена и None иначе.
Теперь мы хотим получать возраст пользователя по ключу.

def ageFromKey(key: Int): Option[Int] = findPerson(key).map(_.age)

Нам не пришлось тестировать на None и метод получился очень лаконичным.

Pattern matching. В начале я подумал, что это тот же джавовский switch и использовать я его почти не буду, но это не так. В Scala это одна из самых часто используемых конструкций.

Приведение типов:


obj match {
      case str: String => str
      case number: Int => number
}

Можно добавить дополнительные условия и добавить действие по умолчанию


obj match {
      case strWithA: String if strWithA.contains("a") => strWithA
      case negative: Int if negative < 0 => negative
      case zero if zero == 0 => zero
      case _ => defaultAction
}

Крайне удобно использовать pattern-matching с case-классами. Пример из Scala Beginning


Stuff("David", 45) match {
      case Stuff("David", age) if age < 30 => "young David" 
      case Stuff("David", _) => "old David"
      case _ => "Other"
}

Функции

Начнем с того, что в Scala функции — это инстансы, реализующие определенный интерфейс, а точнее trait FunctionX, где X это количество параметров и принимает значение от 1 до 22. В этом трейте единственный метод — aplly, который и вызывается для функции. Поскольку функции это обычные инстансы, то мы можем передавать и возвращать их из методов и функций.
Пример из Scala Beginning. Передаем в answer какую-то функцию из Int в String, а она возвращает результат работы этой функции с параметром 42.


def answer(f: Function1[Int, String]) = f(42)

или что тоже самое


def answer(f: Function1[Int, String]) = f.apply(42)

Очень удобная вещь функциональные комбинаторы/

Оставить в массиве только положительные элементы:


arr.filter(value => value > 0)

Чуть более сложный пример. Посчитать сумму значений функции f от положительных элементов списка. 2 способа это сделать:


list.filter(x => x > 0).foldLeft(0)(_ + f(_))
list.filter(x => x > 0).map(f).sum

А в конце хотелось бы сказать зачем вообще я все это написал. Я не хотел никого учить Scala или рассказывать о языке как о таковом, таких статей на хабре и в интернете достаточно много, и многие очень хорошие и полезные. Моей целью было просто рассказать свою историю, которая кому-то может быть интересна и сможет помочь и поддержать какого-нибудь такого же потерянного новичка, которого судьба только-только столкнула с этой скалой. Желаю тебе удачи! Опытных же программистов призываю поделиться в комментариях своими советами и мнениями, на счет пути начинающего Scala-программиста.
Метки:
scala, java, study, job, first steps