Многострочный литерал функции в качестве аргументов в Scala

Я всегда задавался вопросом, почему иногда с литералами функций мы можем игнорировать фигурные скобки даже для нескольких операторов. Чтобы проиллюстрировать это, синтаксис для литерала многострочной функции заключается в заключении операторов с фигурными фигурными скобками. Таким образом,

val fl = (x: Int) => {
  println("Add 25 to "+x)
  x + 25
}

Однако, когда вы передаете его функции с одним аргументом, вы можете игнорировать требуемую фигурную скобку для литерала функции.

Итак, для данной функции f,

def f( fl: Int => Int ) {
  println("Result is "+ fl(5))
}

Вы можете вызвать f() следующим образом:

f( x=> {
  println("Add 25 to "+x)
  x + 25
})
-------------------------
Add 25 to 5
Result: 30

Или, когда вы используете фигурные скобки вместо скобок в вызове функции, вы можете удалить внутренние фигурные фигурные скобки из литерала функции. Таким образом, следующий код также будет работать,

f{ x=>
  println("Add 25 to "+x)
  x + 25
}

Вышеприведенный код более читабельен, и я замечаю, что многие примеры используют этот синтаксис. Однако есть ли какое-то специальное правило, которое я, возможно, пропустил, чтобы объяснить, почему это работает по назначению?

Ответ 1

Есть только несколько простых правил синтаксиса. Приложение спецификации заслуживает внимания.

Функциональная литеральная или анонимная функция (6.23) будет выглядеть как x => Expr или x => Block в зависимости от того, является ли контекст Expr или ResultExpr соответственно.

Приложение-функция (6.6) будет выглядеть как f(Expr, Expr) или f BlockExpr, т.е. f{ Block }. То есть, BlockExpr - это всего лишь последовательность операторов блока внутри {...}.

Когда вы вызываете f(g), тогда g является Expr, поэтому как литерал функции, x => Expr. Expr может быть BlockExpr, x => { ... }.

Когда вы вызываете f{ Block }, тогда f { x => ... } имеет литерал функции в ResultExpr блока (который представляет собой всего лишь последовательность инструкций, без привязок).

Здесь очевидно, что функция anon func находится в нижней части блока:

scala> def m(x: Int=>Int) = x(5)
m: (x: Int => Int)Int

scala> m {
     | val y = 7
     | x => // no brace
     | x+y+1
     | }
res0: Int = 13

Ответ 2

Это одна из вещей, которые делают Scala красивой для меня.

Простой ответ на ваш вопрос:

Круглые скобки() предназначены для однострочных конструкций. Например, это работает:

  def f(fl: Int => Int) {
    println("Result is " + fl(5))
  }

  f(
   x =>
    x + 25)

  f(x => x + 25) // single line

и фигурные скобки {} предназначены для многострочных операторов. Например, это работает:

 f { 
   x =>
     println("Add 25 to " + x)
     x + 25
 }   

но этот код не работает:

f ( 
  x =>
    println("Add 25 to " + x)
    x + 25
)

Компилятор жалуется на следующее сообщение:

Значение x не является членом возможной причины Unit: возможно, точка с запятой отсутствует перед значением `x '?

Если вы добавите точку с запятой, вы получите синтаксическую ошибку, вызванную несогласованной скобкой.

Если вы попытаетесь сделать это:

f { x => println("Add 25 to " + x) x + 25 }

Компилятор вернется к вам с сообщением:

value x is not a member of unit

Вы понимаете, что он пытается найти x в качестве члена подразделения. Например:

f { println("Add 25 to " + x).x.+(25) }

Это явно неправильно.

Если вы добавите внутренние фигурные скобки, как это:

f ( 
  x => {
    println("Add 25 to " + x)
    x + 25 
  }
)

Это также будет работать, но у вас все еще есть многострочный оператор, который сигнализируется использованием фигурных скобок. Поэтому компилятор знает, что вы хотите, чтобы сначала распечатать, а затем добавить 25 в x.

Я уже укусил эти тонкости. С тех пор я обращал внимание на то, как я кодирую их, потому что вы будете кодировать и читать много, когда вы в основном используете карты, плоские карты, foreachs, fors и currying.

Ура!