스칼라의 By-value parameter(값에 의한 파라미터), By-name parameter (이름에 의한 파라미터)의 개념과 활용 예시

View: 180 0 0
작성자: 달빛제이크
카테고리: Scala Language
발행: 2024-06-29 수정 2024-06-29

안녕하세요. 달빛제이크입니다.

이번 글에서는 스칼라 언어에서 지원하는 파라미터의 종류에 대해 살펴보겠습니다. 스칼라에서 지원하는 파라미터의 종류에는 크게 두 가지가 있습니다. 하나는 우리가 항상 예제에서 봤던 By-value parameter, 값에 의한 파라미터이고, 다른 하나는 By-name parameter, 이름에 의한 파라미터입니다. 두 파라미터의 차이점에 대해서 알아보고 이름에 의한 파라미터의 개념과 활용 방법에 대해 이야기하겠습니다.

1. By-value parameter (값에 의한 파라미터)

먼저 By-value parameter는 값을 전달하는 파라미터입니다. 우리가 지금까지 보았던 예제들은 모두 By-value parameter를 사용했습니다. 이 파라미터의 특징은 인수를 전달할 때 미리 계산된 값을 함수에 전달한다는 점입니다. 숫자, 문자열, 문자, Boolean과 같은 값들 외에 연산자가 포함된 모든 수식들, 호출된 함수들 모두 계산(evaluate)된 결과가 함수로 전달됩니다.

만약 미리 계산된 결과를 전달하지 않고, 호출한 함수 내부에서 계산할 필요가 있다면 어떻게 해야 할까요? 예를 들면 Side effect 중 대표적인 println 문을 사용해야 하거나, 미리 평가할 필요가 없는 함수를 전달해야 하거나, 어떤 조건이 충족될 때만 계산해야 하는 경우들 말입니다. 사실 이와 같은 경우에 By-value parameter에 함수 리터럴을 전달하면 동일한 효과를 얻을 수 있습니다. 그런데 스칼라에서는 이마저도 더 간편한 방법을 제공합니다. 함수 리터럴에 파라미터가 없는, 즉 빈 파라미터 리스트를 가지고 있는 함수 리터럴 타입 () => ???이라면 By-name parameter를 사용할 수 있습니다.

2. By-name parameter (이름에 의한 파라미터)

By-name parameter의 첫 번째 목적은 제어 추상화를 좀 더 내장된 제어 구조처럼 보이고자 함에 있습니다. By-name parameter를 정의하기 위해 먼저 함수의 해당 파라미터 부분을 x: () => ??? 대신 x: => ???로 작성합니다. 함수 리터럴 형식에서 ()가 생략된 모습입니다. 그리고 제어 추상화의 몸통을 이루는 함수, 즉 함수 리터럴이 빈 파라미터 목록을 가지고 있다면 앞에 있는 () => 부분을 생략할 수 있습니다.

// By-value parameter 예제
def byValueExample(x: () => Int): Unit = 
  println("Inside byValueExample")
  println("Value of x: " + x())

byValueExample { () =>
  println("Calculating...")
  42 }    // println 문이 실행됩니다.

// By-name parameter 예제
def byNameExample(x: => Int): Unit =
  println("Inside byNameExample")
  println("Value of x: " + x)

// 빈 파라미터 리스트 `() =>` 생략
byNameExample {
  println("Calculating...")
  42 }    // '() =>'를 생략했을 뿐인데 함수 리터럴과 동일한 기능을 하지만 좀 더 자연스러워졌습니다.

// 숫자만 전달 할 경우
byNameExample(1 + 2)    // 실제 연산은 byNameExample 내부에서 진행합니다.

좀 더 자연스러워 보이고, 좀 더 기본으로 제공되는 문법처럼 보입니다. 그런데, 이게 전부 일까요? 다음 예제를 살펴 보겠습니다.

val assertionsEnabled = true

def byValueAssert(predicate: Boolean) =
  if assertionsEnabled && !predicate then
    throw new AssertionError

def byNameAssert(predicate: => Boolean) =
  if assertionsEnabled && !predicate then
    throw new AssertionError

byValueAssert( 5 > 3 )    // 문제 없이 동작
byNameAssert(5 > 3)    // 문제 없이 동작

byValueAssert( 1 / 0 == 0 )    // 호출 전 분모 0에 의한 ArithmeticException 발생
byNameAssert( 1 / 0 == 0 )    // assertionsEnabled가 true이면 호출 이후 Exception 발생, false이면 정상 동작

본 예제는 Programming is Scala에서 발췌한 예제입니다. 마지막 호출 부분이 인상적인데, 분모가 0인 수식을 인수로 사용해서 Exception을 유도하였습니다. By-value parameter는 당연히 호출 전에 Exception이 발생하지만, By-name parameter를 적용했을 경우, assertionsEnabled의 상태에 따라서 달리 동작합니다. true일 경우 Exception이 발생하지만, false일 경우 이미 조건문의 결과가 확정되어 그 뒤의 predicate에 해당하는 1 / 0 == 0 수식이 실행되지 않고 함수 실행이 마무리 됩니다. 이와 같이 By-name parameter에 전달된 수식이나 함수를, 호출 받은 함수 내부에서 사용해야 할 경우는 다음과 같습니다.

1) 전달된 인수를 조건부로 실행해야 할 때
2) 반복 작업이 필요할 때
3) Side effect를 수행할 때

By-name parameter는 빈 파라미터 리스트를 가지고 있는 함수 리터럴을 인수로 사용할 때 적용하는 파라미터이고, 함수 값으로 존재하다가 함수 본체에서 호출될 때 실행이 됩니다. 주로 전달된 인수를 조건부로 실행하거나, 반복 작업이 필요할 때, Side Effect를 실행할 때 활용합니다.

지금까지 By-name parameter를 마지막으로 제어 추상화에 대해 알아 보았습니다. 다음 글에서는 이터레이터와 함수형 변환에 대해서 이야기 하겠습니다. 감사합니다.

comments 0