Scala3 Given 첫번째 - 기본 사용법
View: 145
0
0
작성자: 코딩!제이크
카테고리: Scala Language
발행: 2024-10-19
수정
2024-12-22
함수의 동작은 이벤트가 일어나는 조건 또는 환경 (Context)에 따른 데이타와 알고리즘에 의존하며, 함수에 정보와 동작을 전달하는 다양한 방법 중 함수형 프로그래밍에서 전통적으로 사용하는 방식은 파라미터로 모든 것을 전달하는 것입니다.
Functional behavior depends on
-. Contextual data : System properties, Security permissions, Authenticated user, Database Transaction, Configured timeout
-. Contextual behavior : algorithm
=> 파라미터로 필요한 모든 것들을 전달
더 일반적이고 유용한 함수를 만들기 위해서는 필요한 데이터와 알고리즘을 세분화해서 매 실행 때마다 인수로 전달해야 하는데 이것은 반복 적이고, 필요 없는 코드들을 만들어내게 됩니다. 이에 Scala 언어에서는 givens라고 부르는 context 파라미터를 통해 문맥 상 필요한 인수를 생략해도 컴파일러에서 알맞은 값을 자동으로 채워 주는 방식을 제공하고 있습니다.
기본 사용법
다음의 코드를 살펴보겠습니다.
// 출처: Programming is Scala Fifth Edition
class PreferredPrompt(val preference: String)
object JillsPrefs:
given prompt: PreferredPrompt =
PreferredPrompt("Your wish> ")
object Greeter:
def greet(name: String)(using prompt: PreferredPrompt) =
println(s"Welcome, $name. The system is ready.")
println(prompt.preference)
이 코드의 목적은 object Greeter의 greet이라는 method에 자체 파라미터로 받은 name 값을 이용해 환영 메세지를 보내고 이어서 class PreferredPrompt를 통해 prompt로 예상되는 preference 값을 출력하는 것입니다.
여기서 주목해야 할 곳은 (using prompt: PreferredPrompt)
인데, 만약 PreferredPrompt 클래스로 부터 받고자 하는 값이 이미 정해져 있어서 굳이 인수로 전달 받을 필요가 없다면 using keyword를 통해 greet 메소드를 호출할 때 해당 인수를 생략할 수 있습니다.
using을 통해서 greet 함수가 PreferredPrompt라는 특정 타입의 Context를 사용한다는 점을 명시해 주어 컴파일러는 greet 호출 시점에서 해당 타입에 대한 적절한 context 값을 자동으로 바인딩해 줍니다.
이 때 필요한 것이 given으로, using을 통해 구분되는 Context parameter를 정의하는 역할을 수행합니다.
given 키워드는 val이나 var과 동일한 위치에 작성하며, 변수를 선언하는 방식과 동일하게 사용됩니다. 다만, 문법 상의 역할을 고려하면 변수의 이름보다 타입이 더 중요하기 때문에 타입 이름을 반드시 명시하여야 합니다. 또한 given으로 선언된 값은 전역적으로 존재하는 값이 아니기 때문에 명시적으로 import해야 사용할 수 있습니다.
위의 코드를 통해 object Greeter의 greet 메소드를 Context parameter를 활용해서 호출하기 위해서는 다음과 같이 사용할 수 있습니다.
// 1. import 활용 단일 파라미터
import JillsPrefs.prompt
Greeter.greet("Jill")
// 2. import 활용 복수의 파라미터
import JillsPrefs.given
Greeter.greet("jill")
// 3. 인수로 전달
Greeter.greet("jill")(using JillsPrefs.prompt)
3번째 방식은 Context 파라미터를 사용하는 목적에 맞지 않으니 1번과 2번 방식을 주로 사용합니다.
다음은 복수 개의 Context 파라미터를 사용하는 예제입니다.
// 출처: Programing in Scala Fifth Edition
class PreferredPrompt(val preference: String)
class PreferredDrink(val preference: String)
object Greeter:
def greet(name: String)(using prompt: PreferredPrompt, drink: PreferredDrink) =
println(s"Welcome, $name. The system is ready.")
print("But while you work, ")
println(s"why not enjoy a cup of ${drink.preference}?")
println(prompt.preference)
object JoesPrefs:
given prompt: PreferredPrompt =
PreferredPrompt("relax> ")
given drink:PreferredDrink =
PreferredDrink("tea")
// 호출 예제 (1)
import JoesPrefs.{prompt, drink}
Greeter.greet("Joe")
// 호출 예제 (2)
import JoesPrefs.given
Greeter.greet("Joe")
// 호출 예제 (3)
Greeter.greet("Joe")(using JoesPrefs.prompt, JoesPrefs.drink)
given으로 정의된 prompt와 drink의 이름이 using으로 구분된 파라미터 명과 같을 필요는 없습니다.
메소드에 using 키워드를 사용해서 context 파라미터로 지정하기 위해서는 해당 타입이 import 되어 사용할 수 있어야 합니다.
given으로 정의된 context 파라미터는 Context 파라미터가 정의된 메소드를 호출할 때
사용됩니다.
참고: 도입 배경
Scala 3에서 도입된 given/using 키워드는 Scala 2.x의 implicit 키워드의 모호성을 개선하고자 도입되었습니다.
implicit 키워드는 암시적 파라미터와 암시적 변환에 함께 사용된 키워드이기 때문에 코드를 읽을 때 어떤 역할을 하는 지 직관적으로 알기가 어려웠습니다.
이에 가독성을 개선하고 더 명확한 문법을 제공하고자 given 키워드와 함께 using 키워드와 Conversion 타입을 제공하여 암시적 파라미터와 암시적 변환을 구분하고, import 문을 통해 코드를 Trace 하지 않더라도 해당 코드 내에서 그 쓰임새를 명시적으로 알 수 있도록 하였습니다.
특히 동일한 scope 내에 중복된 암시적 값이 존재했을 때 어떤 값이 실제로 사용이 되었는지 파악이 어려울 수 있고, 암시적 변환의 지나친 사용 또한 코드의 의도를 이해하기 어렵고 예상치 못한 오류 발생 가능성을 만들 수 있기 때문에 1) 명확한 역할 구분, 2) 명시적인 Import, 3) 타입 명시를 통해 implicit의 강력한 기능을 유지하면서 모호성과 복잡성을 해결하는 방향으로 개선이 이루어졌습니다.
이 번 글에서는 간단히 String value를 전달하는 기본 사용법에 대해서 알아보았습니다.
다음 글에서는 좀 더 난이도를 높여서 여러 타입에 대응하는 given 사용 예를 살펴보겠습니다.
