함수를 반환하는 메서드 작성하기
함수를 반환하는 메서드 작성하기 (Creating a Method That Returns a Function)
스칼라에서 함수는 일급 값이라서, 메서드가 뭔가를 반환할 때 문자열이나 정수뿐 아니라 함수도 반환할 수 있어요. 지금까지 배운 것들과 크게 다르지 않아요. 함께 따라가면서 익숙해질게요.
출처: Scala 3 Book
본문
스칼라의 일관성 덕분에, 함수를 반환하는 메서드를 작성하는 것은 지금까지 배운 것들과 아주 비슷해요. 예를 들어 greet라는, 함수를 반환하는 메서드를 작성하고 싶다고 상상해 봐요. 다시 한번 문제 정의부터 시작할게요.
저는 함수를 반환하는
greet메서드를 만들고 싶어요. 그 함수는 문자열 파라미터를 받아println으로 그 문자열을 출력할 거예요. 첫 예제를 단순하게 만들기 위해,greet는 입력 파라미터를 받지 않고 그냥 함수를 만들어서 반환하기만 할 거예요.
이 정의를 바탕으로 greet를 만들기 시작할 수 있어요. 이것이 메서드라는 건 알고 있죠.
def greet()
또한 이 메서드는 (a) String 파라미터를 받고, (b) println으로 그 문자열을 출력하는 함수를 반환할 거라는 것도 알아요. 따라서 그 함수의 타입은 String => Unit이 돼요.
def greet(): String => Unit = ???
----------------
이제 메서드 본문만 작성하면 돼요. 메서드가 함수를 반환해야 하고, 그 함수는 String을 받아 출력한다는 걸 알고 있으니, 이 익명 함수(anonymous function)가 정확히 그 설명과 맞아요.
(name: String) => println(s"Hello, $name")
이제 메서드에서 그 함수를 반환하기만 하면 돼요.
// a method that returns a function
def greet(): String => Unit =
(name: String) => println(s"Hello, $name")
이 메서드는 함수를 반환하므로, greet()를 호출하면 함수를 얻게 돼요. 이 단계는 REPL에서 직접 해 보면 새 함수의 타입을 확인할 수 있어서 좋아요.
scala> val greetFunction = greet()
val greetFunction: String => Unit = Lambda....
-----------------------------
이제 greetFunction을 호출할 수 있어요.
greetFunction("Joe") // prints "Hello, Joe"
축하해요, 함수를 반환하는 메서드를 만들고, 그 함수를 실행해 봤어요.
메서드 개선하기
인사말(greeting)을 넘길 수 있다면 이 메서드가 더 유용할 테니, 그렇게 만들어 볼게요. 해야 할 일은 인사말을 greet 메서드의 파라미터로 넘기고, println 안쪽의 문자열에서 그 인사말을 쓰는 것뿐이에요.
def greet(theGreeting: String): String => Unit =
(name: String) => println(s"$theGreeting, $name")
이제 메서드를 호출할 때 인사말을 바꿀 수 있어서 더 유연해졌어요. 이 메서드에서 함수를 만들면 이렇게 보여요.
scala> val sayHello = greet("Hello")
val sayHello: String => Unit = Lambda.....
------------------------
REPL의 타입 시그니처 출력은 sayHello가 String 입력 파라미터를 받아 Unit(아무것도 아님)을 반환하는 함수임을 보여줘요. 이제 sayHello에 String을 넘기면 인사말을 출력해요.
sayHello("Joe") // prints "Hello, Joe"
원한다면 인사말을 바꿔 새 함수를 만들 수도 있어요.
val sayCiao = greet("Ciao")
val sayHola = greet("Hola")
sayCiao("Isabella") // prints "Ciao, Isabella"
sayHola("Carlos") // prints "Hola, Carlos"
더 현실적인 예제
이 기법은 메서드가 여러 개의 가능한 함수 중 하나를 반환할 때, 즉 커스텀으로 만들어진 함수를 반환하는 *팩토리(factory)*처럼 동작할 때 훨씬 유용해져요.
예를 들어, 다양한 언어로 인사하는 함수들을 반환하는 메서드를 작성하고 싶다고 상상해 봐요. 여기서는 메서드로 전달되는 파라미터에 따라 영어나 프랑스어로 인사하는 함수만으로 한정할게요.
먼저 알 수 있는 건, (a) "원하는 언어"를 입력으로 받고, (b) 결과로 함수를 반환하는 메서드를 만들고 싶다는 거예요. 그리고 그 함수는 주어진 문자열을 출력하므로 타입이 String => Unit이라는 것도 알죠. 이 정보로 메서드 시그니처를 작성해요.
def createGreetingFunction(desiredLanguage: String): String => Unit = ???
다음으로, 반환할 함수들이 문자열을 받아 출력한다는 걸 알고 있으니, 영어와 프랑스어용 익명 함수 두 개를 작성할 수 있어요.
(name: String) => println(s"Hello, $name")
(name: String) => println(s"Bonjour, $name")
메서드 안쪽에서는 익명 함수에 이름을 붙이면 좀 더 읽기 좋을 테니, 두 변수에 할당해 볼게요.
val englishGreeting = (name: String) => println(s"Hello, $name")
val frenchGreeting = (name: String) => println(s"Bonjour, $name")
이제 해야 할 일은 (a) desiredLanguage가 영어면 englishGreeting을, (b) 프랑스어면 frenchGreeting을 반환하는 것뿐이에요. 그중 한 가지 방법은 match 표현식을 쓰는 거예요.
Scala 2 버전
def createGreetingFunction(desiredLanguage: String): String => Unit = {
val englishGreeting = (name: String) => println(s"Hello, $name")
val frenchGreeting = (name: String) => println(s"Bonjour, $name")
desiredLanguage match {
case "english" => englishGreeting
case "french" => frenchGreeting
}
}
Scala 3 버전
def createGreetingFunction(desiredLanguage: String): String => Unit =
val englishGreeting = (name: String) => println(s"Hello, $name")
val frenchGreeting = (name: String) => println(s"Bonjour, $name")
desiredLanguage match
case "english" => englishGreeting
case "french" => frenchGreeting
이게 최종 메서드예요. 메서드에서 함수 값을 반환하는 것이 문자열이나 정수 값을 반환하는 것과 다를 바 없다는 점을 눈여겨보세요.
이렇게 createGreetingFunction이 프랑스어 인사 함수를 만들어요.
val greetInFrench = createGreetingFunction("french")
greetInFrench("Jonathan") // prints "Bonjour, Jonathan"
그리고 이렇게 영어 인사 함수를 만들어요.
val greetInEnglish = createGreetingFunction("english")
greetInEnglish("Joe") // prints "Hello, Joe"
이 코드가 편안하게 느껴진다면 — 축하해요, 함수를 반환하는 메서드를 작성하는 방법을 익힌 거예요.
더 알아보기 (Learn more)
- Scala 3 Book — Functions — 함수 소개
- Scala 3 Book — Higher-Order Functions — 고차 함수