완화된 람다 문법

완화된 람다 문법 (Relaxed Lambda Syntax)

함수 리터럴을 더 유연한 방식으로 쓸 수 있게 해주는 여러 작은 개선을 합친 실험적 추가 기능이에요. 한 줄 람다, 커리된 람다, 괄호 안/최상위 case 표현식 등을 지원해요.

출처: Scala 3 Reference

본문

이 실험적 추가 기능은 함수 리터럴을 더 유연한 방식으로 쓰기 위한 작은 개선 몇 가지를 결합해요. 이 개선들은 SIP 74와 SIP 75에 명세되어 있어요. 실험적 언어 import로 활성화됩니다:

import language.experimental.relaxedLambdaSyntax

한 줄 람다 (Single Line Lambdas)

: 뒤에 같은 줄에 오는 람다 표현식이 이제 지원돼요. 이전에는 화살표 뒤에 줄바꿈과 들여쓰기가 필요했어요. 예를 들어:

xs.map: x =>
  x + 1

이제 람다를 한 줄에 쓸 수도 있어요:

xs.map: x => x + 1

이 경우 람다는 줄 끝까지 확장돼요.

이 문법은 모든 종류의 함수 리터럴에서 동작해요. 하나 이상의 파라미터로 시작하거나, 타입 파라미터로 시작하거나, case로 시작하는 부분 함수일 수 있어요.

Seq((1, 2), (3, 4)).map: (a, b) => a + b

Seq((1, 2), (3, 4)).map: (a: Int, b: Int) => a + b

Seq((1, 2), (3, 4)).collect: case (a, b) if b > 2 = a

(1, true).map: [T] => (x: T) => List(x)

이 문법은 =>?=>를 담지 않은 함수 값에는 동작하지 않아요. 예를 들어 다음은 불법이에요.

Seq((1, 2), (3, 4)).map: _ + _ // error

Seq(1, 2, 3).map: plus1 // error

한 줄 람다는 중첩될 수 있어요. 다음과 같이요:

xs.map: x => x.toString + xs.dropWhile: y => y > 0

상세 명세 (Detailed Spec)

:는 다음 중 하나가 뒤따르면 애플리케이션(적용)을 뜻해요:

  • 줄 끝과 들여쓰기된 블록,
  • 파라미터 섹션, 그다음 => 또는 ?=>, 줄 끝과 들여쓰기된 블록,
  • 파라미터 섹션, 그다음 => 또는 ?=>와 한 줄에 있는 표현식,
  • 단일 case 부분 함수를 나타내는 case 절.

(1)과 (2)는 현행 상태이고, (3)과 (4)가 새로워요.

제약: (3)과 (4)는 괄호로 즉시 둘러싸인 코드에는 적용되지 않아요(더 가깝게 중괄호나 들여쓰기로 묶이지 않은 경우). 이는 타입 애스크립션(타입 지정)과의 모호성을 피하기 위해서예요. 예를 들어

(
  x: Int => Int
)

는 여전히 타입 애스크립션을 뜻하고, 함수 애플리케이션으로 해석하려 시도하지 않아요.

커리된 여러 줄 람다 (Curried Multi-Line Lambdas)

이전에는 들여쓰기된 블록 앞에 단일 파라미터 섹션과 화살표만 허용했어요. 이제 여러 개의 그런 섹션도 허용합니다. 그래서 다음이 이제 합법적이에요:

def fun(f: Int => Int => Int): Int = f(1)(2)

fun: (x: Int) => y =>
  x + y

위 상세 명세에서 항목 (2)는 다음과 같이 수정됩니다:

  • 하나 이상의 파라미터 섹션, 각각 => 또는 ?=>가 뒤따르고, 마지막으로 줄 끝과 들여쓰기된 블록.

case 표현식 (Case Expressions)

이전에는 부분 함수를 구성하는 case 절이 중괄호나 들여쓰기된 블록 안에 쓰여야 했어요. 단일 case 절만 있으면, 이제 괄호 안이나 최상위 표현식으로도 쓸 수 있게 허용합니다.

예시:

case class Pair(x: Int, y: Int)

Seq(Pair(1, 2), Pair(3, 4)).collect(case Pair(a, b) if b > 2 => a)

Seq(Pair(1, 2), Pair(3, 4)).collect(
  case (a, b) =>
    println(b)
    a
)

val partial: PartialFunction[(Int, Int), Int] = case (a, b) if b > 2 => a

문법 변경 (Syntax Changes)

Expr          ::= ...
               |  ExprCaseClause

ColonArgument ::= colon {LambdaStart} indent (CaseClauses | Block) outdent
               |  colon LambdaStart {LambdaStart} expr ENDlambda
               |  colon ExprCaseClause

여기서 ENDlambda는 프로덕션을 시작하는 토큰 뒤에 오는 줄의 다음 줄 끝(다음 행 끝)에서 합성되는 토큰이에요.

ExprCaseClause는 이미 문법에 존재해요. 다음과 같이 정의됩니다:

ExprCaseClause  ::=  ‘case’ Pattern [Guard] ‘=>’ Expr