217
논란의 여지는 있지만, 스칼라의 암시
implicit
는 강력한 기능이다. 암시를 사용하면 준비 코드를
줄이고, 기존 타입에 새로운 메서드를 추가한 것과 같은 효과를 낼 수 있고, 도메인 특화 언어
domain
specific
language
(
DSL
)의 생성을 지원할 수 있다.
암시가 논란의 대상이 되는 이유는 암시를 찾을 때 소스 코드에서 ‘지역적이 아닌’ 부분까지 고
려하기 때문이다. 여러분은
Predef
(
http
://
bit
.
ly
/
1086O2z
)에서 자동으로 임포트되는 경우
를 제외한 나머지 암시적 값이나 메서드를 지역 범위에 임포트한다. 컴파일러는 범위 안에 들
어온 암시를 호출해서 메서드에 인자를 제공하거나, 인자 중 일부를 원하는 타입으로 변환한
다. 하지만 소스 코드를 읽을 때는 어떤 암시적 값이나 메서드를 활용하고 있는지 분명히 알아
보는 것이 쉽지 않고, 그래서 혼란에 빠질 수 있다. 다행히 경험을 통해 어떤 경우에 암시가 관
련이 있는지 알 수 있고, 암시를 활용하는
API
를 배우게 된다. 그럼에도 불구하고 초보자들은
함정에 빠지기 마련이다.
암시가 어떻게 작동하는지 이해하는 것은 상당히 단순하다. 이번 장은 꽤 길지만, 그중 대부분
은 암시가 해결할 수 있는 설계 문제를 다루고 있다.
5.1
암시적 인자
2
.
5
.
3
절 ‘
Future
맛보기’에서
implicit
라는 키워드를 이미 살펴봤다. 메서드 인자 중 사용자가
암시
CHAPTER
5
218
2
부
기본기 다지기
명시적으로 제공할 필요가 없는 인자를 표시하기 위해 그 키워드를 사용했다. 암시적 인자를 생
략하는 경우, 그 문장을 둘러싸는 범위에서는 타입이 호환되는 값을 제공해야 한다. 그렇지 않은
경우 컴파일 오류가 발생한다.
세율이 암시적 인자인 거래세를 계산하는 메서드가 있다고 하자.
def calcTax(amount: Float)(implicit rate: Float): Float = amount * rate
이 메서드를 호출하는 코드에서는 지역 범위에 있는 암시적 값을 사용한다.
implicit val currentTaxRate
=
0
.
08F
...
val tax
=
calcTax
(
50000F
)
//
4000
.
0
간단한 경우에는 고정된
Float
값으로 충분할 것이다. 하지만 애플리케이션이 거래가 일어난 장
소를 알아야 할 필요도 있을 것이다. 예를 들면 지방세에 추가하는 경우를 들 수 있다. 일부 자
치 단체에서는 세밑의 쇼핑을 장려하기 위해 ‘세금 휴일’을 제공할 수도 있다.
다행히 암시적 메서드도 사용할 수 있다. 암시적 값과 마찬가지로 작동하기 위해서는 인자가 암
시적이 아니라면 인자 자체를 받아서는 안 된다. 인자가 필요하다면 인자를 암시적으로 만들어
야 한다. 다음은 거래세를 계산하는 완전한 코드다.
//
src
/
main
/
scala
/
progscala2
/
implicits
/
implicit
-
args
.
sc
//
실제
통화
계산에는
Float
등의
부동소수점수를
사용하지
말라
.
def calcTax
(
amount
:
Float
)(
implicit rate
:
Float
):
Float
=
amount
*
rate
object SimpleStateSalesTax
{
implicit val rate
:
Float
=
0
.
05F
}
case class ComplicatedSalesTaxData
(
baseRate
:
Float
,
219
5
장
암시
isTaxHoliday
:
Boolean
,
storeId
:
Int
)
object ComplicatedSalesTax
{
private def extraTaxRateForStore
(
id
:
Int
):
Float
=
{
//
id
로부터
위치를
정하고
추가
세금
등을
계산한다
.
0
.
0F
}
implicit def rate
(
implicit cstd
:
ComplicatedSalesTaxData
):
Float
=
if
(
cstd
.
isTaxHoliday
)
0
.
0F
else cstd
.
baseRate
+
extraTaxRateForStore
(
cstd
.
storeId
)
}
{
import SimpleStateSalesTax
.
rate
val amount
=
100F
println
(
s
"
Tax on
$
amount
=
${
calcTax
(
amount
)}")
}
{
import ComplicatedSalesTax
.
rate
implicit val myStore
=
ComplicatedSalesTaxData
(
0
.
06F
,
false
,
1010
)
val amount
=
100F
println
(
s
"
Tax on
$
amount
=
${
calcTax
(
amount
)}")
}
calcTax
를 문자열 인터폴레이션 내에서 호출해도 아무 문제가 없다. 그래도 여전히
rate
인자
에 암시적 값을 사용한다.
‘복잡한’ 경우에는 암시적 메서드를 사용한다. 암시적 메서드는 암시적 인자를 받아서 필요한 데
이터를 처리할 수 있다.
위 스크립트를 실행하면 다음 출력을 얻을 수 있다.
Tax on 100
.
0
=
5
.
0
Tax on 100
.
0
=
6
.
0
220
2
부
기본기 다지기
5.1.1
implicitly
사용하기
Predef
(
http
://
bit
.
ly
/
1086O2z
)에는
implicitly
라는 메서드 정의가 있다. 이 메서드와 타입
시그니처를 조합하면, 매개변수화한 타입인 암시적 인자를 단 하나만 사용하는 메서드의 시그
니처를 정의할 때 아주 유용하다.
List
(
http
://
bit
.
ly
/
15iqGNE
)의
sortBy
예제를 감싸는 다음 예제를 살펴보자.
//
src
/
main
/
scala
/
progscala2
/
implicits
/
implicitly
-
args
.
sc
import math
.
Ordering
case class MyList
[
A
](
list
:
List
[
A
])
{
def sortBy1
[
B
](
f
:
A
=
>
B
)(
implicit ord
:
Ordering
[
B
]):
List
[
A
]
=
list
.
sortBy
(
f
)(
ord
)
def sortBy2
[
B
:
Ordering
](
f
:
A
=
>
B
):
List
[
A
]
=
list
.
sortBy
(
f
)(
implicitly
[
Ordering
[
B
]])
}
val list
=
MyList
(
List
(
1
,
3
,
5
,
2
,
4
))
list sortBy1
(
i
=
>
-
i
)
list sortBy2
(
i
=
>
-
i
)
List
.
sortBy
는 다양한 컬렉션에 존재하는 정렬 메서드 중 하나다. 이 메서드는 ‘인자를
math
.
Ordering
을 만족하는 대상으로 변환하는 함수’를 인자로 받는다.
math
.
Ordering
은 자바의
Comparable
(
http
://
bit
.
ly
/
1u1hDR8
) 추상화와 비슷하다. 따라서
B
타입의 인스턴스 순서를
어떻게 지정할지에 대해 아는 암시적 인자가 필요하다.
MyList
는
sortBy
와 비슷한 메서드를 작성하는 두 가지 다른 방법을 보여준다. 첫 번째 구현인
sortBy1
에서는 익히 알고 있는 구문을 활용한다. 메서드는
Ordering
[
B
]
타입의 암시적 값을
추가로 받는다.
sortBy1
을 사용하기 위해서는 범위 안에 원하는
B
타입의 인스턴스 사이의 ‘순
서’를 정하는 방법을 아는 객체가 있어야 한다. 이런 경우
B
타입이 ‘맥락’에 의해 바운드
bound
된
다고 말한다. 여기서는 인스턴스의 순서를 정하는 능력이 타입을 바운드한다.
스칼라에서는 이런 관용구가 널리 쓰이기 때문에, 이를 짧게 쓸 수 있는 방법을 제공하는데, 그
게 바로 두 번째 구현인
sortBy2
다. 타입 매개변수
B
:
Ordering
을 맥락 바운드
context
bound
라고
Get 프로그래밍 스칼라: 실용적인 스칼라 활용법을 익히는 가장 확실한 실전 바이블 (2.11.x 버전 기반) now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.