[Scala] Call-by-value와 Call-by-name의 차이
14 May 2022들어가며
Coursera 수업을 들으면서 Scala의 인자 전달법에 대해 모르던 내용이 나와서 정리한다.
Coursera의 Functional Programming Principles in Scala의 내용 중 일부를 정리하였다.
Call-by-value와 Call-by-name
Scala에서는 함수를 호출할 때 사용하는 인자 전달법으로 Call-by-value, Call-by-name 2가지를 지원한다.
- Call-by-value
Call-by-value는 일반적인 프로그래밍 언어들에서 기본적으로 지원하는 인자 전달법으로, 인자 값을 복사하여 함수에 전달하고, 인자로 표현식이 들어온 경우 표현식을 먼저 계산하고 함수에 전달한다.
Scala에서는 다음과 같이 Call-by-value 인자를 만든다.
def square(x: Double) = x * x
def sumOfSquares(x: Double, y: Double) = square(x) + square(y)
Call-by-value 형식의 Evaluation은 다음과 같다.
sumOfSquares(3, 2+2)
sumOfSquares(3, 4)
square(3) + square(4)
3 * 3 + square(4)
9 + square(4)
9 + 4 * 4
9 + 16
25
그런데 이런 Call-by-value가 문제가 있는 경우가 존재한다.
def loop: Int = loop
def chooseX(x: Int, y: Int) = x
chooseX(3, loop(2))
위 코드에서 chooseX(x, y)는 항상 x를 반환하지만, chosse(3, loop(2))에서 인자들을 먼저 reduce 하는데, loop가 무한루프이기 때문에 에러가 발생한다. 이런 상황은 Call-by-name을 활용하면 해결할 수 있다.
- Call-by-name
Call-by-name은 함수의 인자들을 reduce 하기 전에 함수를 적용한다. 표현식이 들어올 경우 표현식 통째로 함수에 인자로 넣는다.
Scala에서는 다음과 같이 =>를 사용하여 Call-by-name 인자를 만든다.
def square(x: => Double) = x * x
def sumOfSquares(x: => Double, y: => Double) = square(x) + square(y)
Call-by-name 형식의 Evaluation은 다음과 같다.
sumOfSquares(3, 2+2)
square(3) + square(2+2)
3 * 3 + square(2+2)
9 + square(2+2)
9 + (2+2) * (2+2)
9 + 4 * (2+2)
9 + 4 * 4
9 + 16
25
위 Call-by-value의 예에서 Call-by-name을 사용했을 경우는 다음과 같다.
def loop: Int = loop
def chooseX(x: Int, y: => Int) = x
chooseX(3, loop(2))
3
위와 같이 함수에 인자를 넘겨줄 때 Call-by-name 방식을 사용하면 정상적으로 값이 반환된다.
- 상황에 따른 단계 비교
다음 상황에서 Call-by-value와 Call-by-name을 사용했을 때 어디가 더 빠를까?
def test(x: Int, y: Int) = x * x
// 1
test(2, 3)
// 2
test(3+4, 8)
// 3
test(7, 2*4)
// 4
test(3+4, 2*4)
Call-by-value
// 1
test(2, 3)
2 * 2
4
// 2
test(3+4, 8)
test(7, 8)
7 * 7
49
// 3
test(7, 2*4)
test(7, 8)
7 * 7
49
// 4
test(3+4, 2*4)
test(7, 2*4)
test(7, 8)
7 * 7
49
Call-by-name
// 1
test(2, 3)
2 * 2
4
// 2
test(3+4, 8)
(3+4) * (3+4)
7 * (3+4)
7 * 7
49
// 3
test(7, 2*4)
7 * 7
49
// 4
test(3+4, 2*4)
(3+4) * (3+4)
7 * (3+4)
7 * 7
49
항상 Call-by-value가 빠른게 아니고, Call-by-name이 더 빠른 경우도 있다.
마치며
Scala에서 생소한 개념인 Call-by-name에 대해 알아보았다. 아직까지는 Call-by-name을 어떻게 써야 할지 감이 잘 안 온다. 더 깊게 공부하다 보면 써먹을 곳이 있겠지?