Это первая статья в моей серии статей с обзором изменений в Scala 3.
Давайте начнем с наиболее противоречивых нововведений: опциональных фигурных скобок и
нового синтаксиса для управляющих конструкций.
Опциональные фигурные скобки делают Scala-код больше похожим на Python или Haskell, где для группировки выражений используются отступы. Рассмотрим примеры, взятые из 3-го издания моей книги Programming Scala, которое сейчас готовится к публикации.
Опциональные фигурные скобки
Для начала рассмотрим объявление типа с использованием старого и нового синтаксиса. Это также работает для пакетов, если мы объявляем несколько пакетов в одном файле.
// Со скобками
trait Monoid2[A] {
def add(a1: A, a2: A): A
def zero: A
}
// Без скобок
trait Monoid3[A]:
def add(a1: A, a2: A): A
def zero: A
Новый синтаксис сильно напоминает Python, и это может приводить к путанице, если вы постоянно переключаетесь между двумя языками.
Вы можете смешивать старый и новый стиль, компилятор Dotty (скоро будет переименован в Scala 3) скомпилирует такой код без ошибок.
Посмотрим на объявления методов. Обратите внимание, что для маркировки начала тела метода используется =
, а не :
.
def m2(s: String): String = {
val result = s.toUpperCase
println(s"output: $result")
result
}
def m3(s: String): String =
val result = s.toUpperCase
println(s"output: $result")
result
Питонисты будут по привычке писать :
пока не привыкнут к особенностям Scala. Отличие от старого синтаксиса в том, что после =
теперь можно писать не одно, а сколько угодно выражений. Однако придется следить за правильностью отступов, будь то табы или пробелы.
В этом же стиле могут быть переписаны partial functions, match expressions и блоки try-catch-finally (для для последнего примера не будет):
val o2:Option[Int] => Int = {
case Some(i) => i
case None => 0
}
val o3:Option[Int] => Int =
case Some(i) => i
case None => 0
0 match {
case 0 => "zero"
case _ => "other value"
}
0 match
case 0 => "zero"
case _ => "other value"
Исторически в плане синтаксиса Scala старалась держаться как можно ближе к Java. Зачем же делать столь радикальные изменения сейчас? Сегодня нередки случаи, когда люди, знающие Python, начинают изучать Scala. Возможно, они учили Python в университете, а компания, в которую они потом пошли работать, использует Scala. Многие проекты сочетают задачи data science, которые решаются на Python, и data engineering, которые решаются на Scala. В этом смысле стремление Scala быть похожей на Python выглядит интересно.
Вместе с тем, это изменение достаточно спорно. Всегда можно возразить, что и со старым синтаксисом не было особых проблем, а добавления второго варианта может просто укрепить ощущение, что Scala слишком сложна. Есть еще один небольшой недостаток:
import scala.annotation.tailrec
@tailrec def loop(whileTrue: => Boolean)(f: => Unit): Unit =
f
if (whileTrue) loop(whileTrue)(f)
var i=5
loop(i > 0) {
println(i)
i -= 1
}
var j=5
loop(j > 0): // ERROR
println(j)
j -= 1
У фигурных скобок была крутая способность: с их помощью можно было определять собственные "управляющие" конструкции. В примере выше loop
выглядит как встроенный цикл while
. Но попытка сделать тоже самое без скобочек не работает. (Возможно, это добавят в следующих релизах.)
Когда я только начал работать над новым изданием книги Programming Scala, я был против нового синтаксиса и планировал просто рассказать про него, оставив остальной код в старом стиле. Потом, поскольку книга все-таки должна была фокусироваться на Scala 3, я решил использовать новый синтаксис практически повсеместно. Сейчас я уже привык к нему, он даже начал мне нравится. Он делает Scala-код более выразительным. Кроме того, не стоит забывать о трендах в индустрии: если этот синтаксис делает Scala более привлекательной для разработчиков на Python (и Haskell) — это хорошо.
Опциональный синтаксис для управляющих конструкций
Также появился новый синтаксис для управляющих конструкций, таких как if
, for
и while
. Его тоже можно использовать вместе со старым:
for (i <- 0 until 5) println(i) // Старый синтаксис
for i <- 0 until 5 do println(i) // Новый синтаксис
for i <- 0 until 5 yield 2*i
for i <- 0 until 10
if i%2 == 0
ii = 2*i
yield ii
val i = 10
if (i < 10) println("yes") // Старый синтаксис
else println("no")
if i < 10 then println("yes") // Новый синтаксис
else println("no")
Для циклов for
и while
можно убирать круглые и фигурные скобки, отмечая начало тела цикла ключевым словом do
. В for
также можно использовать ключевое слово yield
. Для if
можно убирать круглые скобки и писать then
после условия.
По умолчанию старый и новый стили можно смешивать. Флаг компилятора -new-syntax
обязывает использовать только новый стиль, флаг -old-syntax
— только старый.
Добавлен также флаг -rewrite
, в зависимости от того, в комбинации с каким из двух предыдущих флагов он указан, компилятор будет конвертировать исходный код в новый синтаксис или в старый.
Заключение
Для краткости изложения в этой статье пришлось опустить множество деталей и примеров. Если тема вас заинтересовала, посмотрите полную документацию по ссылкам в начале статьи.
Если у вас уже есть код на Scala 2, его не обязательно будет переписывать в соответствии с новым синтаксисом. И, конечно, я не рекомендую смешивать оба стиля. Выберите для своего проекта один из них. Вряд ли старый Java-подобный синтаксис когда-либо признают устаревшим и удалят из языка, но время покажет.
Самому мне эти изменения начали нравится после того, как я поработал с ними какое-то время. Теперь я буду использовать новый синтаксис во всех своих новых проектах.
nehaev Автор
На данный момент на последний вопрос ответило 25 человек. Это, конечно, сложно назвать репрезентативной выборкой, но то, что количество людей, писавших до Scala на Python и Haskell в сумме больше, чем на Java — для меня сюрприз. Возможно, Одерски не так уж неправ, вводя этот новый синтаксис.
anonymous
Мне кажется, что хаскелисты просто подписаны на тег Scala (или, скажем так, вероятность того, что хаскелист ткнет на эту статью больше, чем для джависта), поэтому так и получилось.
А вообще, разве не легче всё писать без скобок? Питон доказывает, что вполне себе хорошее решение