더 나은 for 컴프리헨션
더 나은 for 컴프리헨션 (Better Fors)
Scala 3.8부터, 또는 Scala 3.7에서 -preview 모드로 쓰면 for 컴프리헨션(for-comprehension)의 사용성이 크게 개선돼요.
본문
사용자에게 가장 크게 보이는 변화는 for 컴프리헨션을 별칭(alias)으로 시작할 수 있게 된 거예요. 덕분에 이전에는 컴파일되지 않던 다음 코드가 이제 유효해져요:
for
as = List(1, 2, 3)
bs = List(4, 5, 6)
a <- as
b <- bs
yield a + b
이 코드의 desugaring(설탕 제거)은 별칭을 val로 도입했을 때와 동일해요:
val as = List(1, 2, 3)
val bs = List(4, 5, 6)
for
a <- as
b <- bs
yield a + b
추가로, 이 확장은 for 컴프리헨션이 desugaring되는 방식을 바꿔요. 이제 desugaring이 더 직관적으로 이루어지고, 불필요한 메서드 호출 일부를 피할 수 있어서 생성되는 코드가 더 효율적일 수 있어요. desugaring에는 두 가지 주요 변화가 있어요.
- 순수 별칭에 대한 더 단순한 desugaring: 별칭 뒤에 가드(guard)가 오지 않으면 desugaring이 단순해져요. 마지막 생성자(generator)와 별칭을 튜플로 감쌀 필요가 없고, 대신 별칭을 다음 생성자와 함께 블록 안의 지역 변수로 그냥 도입해요. 이전의 desugaring은:
for {
a <- doSth(arg)
b = a
} yield a + b
이렇게 desugar돼요:
doSth(arg).map { a =>
val b = a
(a, b)
}.map { case (a, b) =>
a + b
}
새로운 desugaring은:
doSth(arg).map { a =>
val b = a
a + b
}
이 변화는 별칭 뒤에 가드가 오지 않을 때 desugaring을 더 직관적으로 만들고, 불필요한 map 호출을 피할 수 있게 해 줘요.
- 중복
map호출 회피: for 컴프리헨션의 결과가 마지막 생성자 패턴과 같은 표현식일 때, desugaring이 불필요한map호출을 피해요. 단, 마지막 패턴과 결과의 동일성은 구문적으로 확인할 수 있어야 해서, 변수이거나 변수들의 튜플이어야 해요. 본문이()(Unit 상수)를 반환하는 상수 함수(constant function)라면map을 떨어뜨리는 특별한 경우도 있어요. 현재의 desugaring은:
for {
a <- List(1, 2, 3)
} yield a
이렇게 desugar돼요:
List(1, 2, 3).map(a => a)
새로운 desugaring은:
List(1, 2, 3)