Scala 3: новый, но необязательный синтаксис


Это первая статья в моей серии статей с обзором изменений в 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
16.12.2020 16:47 UTC
Первоисточник

Комментарии

@nehaev
16.12.2020 16:38 UTC
0

На данный момент на последний вопрос ответило 25 человек. Это, конечно, сложно назвать репрезентативной выборкой, но то, что количество людей, писавших до Scala на Python и Haskell в сумме больше, чем на Java — для меня сюрприз. Возможно, Одерски не так уж неправ, вводя этот новый синтаксис.

@GeorgeII
16.12.2020 18:57 UTC
0

Мне кажется, что хаскелисты просто подписаны на тег Scala (или, скажем так, вероятность того, что хаскелист ткнет на эту статью больше, чем для джависта), поэтому так и получилось.


А вообще, разве не легче всё писать без скобок? Питон доказывает, что вполне себе хорошее решение

@andy128k
16.12.2020 19:53 UTC
+4
У Скалы и до этого были проблемы с синтаксисом (неоднозначная трактовка в зависимости от пробелов и пустых строк), а теперь ещё и спорный синтаксис с отступами будет.

Поддержка Скалы в IDE и редакторах и так отстаёт от других языков из-за сложняков вроде имплицитов, так теперь ещё и авто-форматирование кода пропадает.

«Опциональность» тут вообще выглядит как издевательство. Забыл открывающую фигурную скобку и всё, твой код уже интерпретируется иначе.

Что же касается популярности языка, то синтаксис тут вторичен. Ставка на то чтобы быть better Java сыграла злую шутку. Скала и её сообщество дали миру много замечательных вещей таких как Spark или Kafka. Но потом Скалу отбросили до вторичной роли «языка реализации» и Java опять вышла на передний план.
@
16.12.2020 22:53 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
17.12.2020 19:30 UTC
0
К сожалению примера, с которым столкнулся на практике, нет под рукой, но вот здесь есть похожий.
@
16.12.2020 22:52 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@nehaev
16.12.2020 23:06 UTC
0

Вроде бы есть попытка пофиксить это. Но у меня не завелось. Возможно, в M2, на которой я тестил, этого еще не было.

15.01.2021 14:06 UTC
0

Пробовал, вроде работало, но мне не понравилось. Среде разработки окончательно сносило крышу. Довольно часто код не становился короче


lst.map(a => a + 1)
lst.map{a => 
    a + 1 
}

vs


lst.map:
    a => 
        a+1

И ещё такие конструкции становится неудобно собирать в цепочку или вкладывать друг в друга.


В итоге я без скобок только небольшое количество конструкций использую:
1) объявления классов и методов (читаемость повышается, закрывающая скобка не нужна)
2) конструкции типа x match:
3) иногда в циклах for, но обычно со скобками лучше

@freecoder_xx
17.12.2020 07:08 UTC
+1

Кстати, на превью пример справа, со старым синтаксисом, выглядит проще, чем пример слева. Замена фигурных скобок на скобки "something ... end something" не выглядит упрощающим чтение улучшением. Теперь нужно сосредотачиваться на этих end, читать их (а вдруг там не end?), тогда как скобки обычно просто воспринимаются в фоне.


Что действительно улучшает читаемость — это отсутствие круглых скобок вокруг условий. Но такой синтаксис совместим и с фигурными скобками, как это сделано в Rust.

@nehaev
17.12.2020 19:31 UTC
0
Теперь нужно сосредотачиваться на этих end, читать их (а вдруг там не end?), тогда как скобки обычно просто воспринимаются в фоне.

Справедливости ради, по гайдлайну этот пример на картинке нужно переписать как-то так:


package newsyntax:

    abstract class C():

        def this(x: Int) =
            this()
            if x > 0 then
                var y = x
                while y > 0 do
                    println(y)
                    y -= 1
            else
                x match
                    case 0 => println("0")
                    case _ =>

    end C
end newsyntax

Это и компактнее и, имхо, читабельнее. На картинке сделано с кучей end просто для понимания соответствия старого кода и нового.

@
17.12.2020 09:37 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@stokker
17.12.2020 13:26 UTC
0
А можно поинтересоваться о причинах такого перехода? Не ожидал, что такой кейс будет популярен…
17.12.2020 15:58 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
17.12.2020 17:50 UTC
0
Да, хорошо работать в одиночку и самому определять стек. Но ведь тогда и рефакторинг не должен быть слишком сложен? Просто интересно…
17.12.2020 20:45 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@Frost1nk
20.12.2020 18:37 UTC
0
Может и есть в этом какой-то смысл, но не для тех, кто перешёл с Явы на Скалу.
У нас в компании попеременно написано 70/30 -> Java/Scala и убрать фигурные скобки, ну крайне сомнительная идея, как по мне.
Хотя в некоторых случаях будет выглядеть интересно и лаконично.
@Yulaw
10.01.2021 19:20 UTC
0
Всем сторонникам и противникам сотступов рекомедую принять участие в обсуждение этой фичи так что бы потом не было обидно, что мнение не было учтено — contributors.scala-lang.org/t/feedback-sought-optional-braces/4702