Месяца назад я получил свою первую работу и стал стажер-разработчиком, наша команда использует язык 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-программиста.
Автор: new player