533
14
장
스칼라 타입 시스템 I
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
)
두 버전의
sortBy
를 비교하면,
sortBy1
에는 암시적인 매개변수가 겉에 드러나 있는 반면,
sortBy2
에 있는 ‘감춰진’ 매개변수는 매개변수화한 타입이다.
B
:
Ordering
이라는 타입 식은
B
에는 아무 조작을 가하지 않고,
Ordering
[
B
]
라는 타입의 암시적 매개변수로 넘기는 것과 같다.
이는 적절한
Ordering
[
B
]
가 존재하지 않는 경우에는
B
라는 구체적인 타입을 사용할 수 없다는
뜻이다.
비슷한 개념으로 뷰 바운드가 있다.
14.4
뷰 바운드
뷰 바운드
view
bound
는 맥락 바운드와 비슷하며, 맥락 바운드의 특별한 경우로 간주할 수 있다. 뷰
바운드는 아래 두 방식 중 한 가지를 사용해서 선언할 수 있다.
class C
[
A
]
{
def m1
[
B
](...)(
implicit view
:
A
=
>
B
):
ReturnType
=
{...}
def m2
[
A
<
%
B
](...):
ReturnType
=
{...}
}
A
:
B
라는 암시적 값이
B
[
A
]
타입이어야 했던 앞의 맥락 바운드의 경우와는 달리, 여기서는
A
를
B
의 한 가지로 바꿔주는 변환 함수가 필요하다. 이를 ‘
B
는
A
에 대한 뷰 중 하나다
B
is
a
view
onto
A
’라고 말한다. 반대로
A
가
B
의 서브타입이어야 하는 상위 바운드 식
A
<
:
B
와 달리, 뷰 바운드
534
3
부
기초를 넘어서
는 좀 더 느슨한 요구 사항이다. 뷰 바운드는
A
가
B
로 변환될 수 있어야 한다고 말한다.
다음은 이런 특징을 어떻게 사용할 수 있는지 간략히 보여준다. 하둡
Hadoop
(
http
://
hadoop
.
apache
.
org
) 자바
API
는 데이터 값을 전용 직렬화기로 감싸도록 요구한다. 이런 직렬화기는
보통
Writable
(
http
://
bit
.
ly
/
1s0TMvi
) 인터페이스를 구현해서 값을 원격 프로세스에 보낸
다. 이
API
의 사용자는 이런
Writable
을 직접 다뤄야 한다. 이는 좀 불편하다. 우리는 뷰 바운
드를 사용해서 이런 처리를 자동으로 할 수 있다 (단순화를 위해 직접
Writable
을 작성했다 ).
//
src
/
main
/
scala
/
progscala2
/
typesystem
/
bounds
/
view
-
bounds
.
sc
import scala
.
language
.
implicitConversions
object Serialization
{
case class Writable
(
value
:
Any
)
{
def serialized
:
String
=
s
"
--
$
value
--
"
//
➊
}
implicit def fromInt
(
i
:
Int
)
=
Writable
(
i
)
//
➋
implicit def fromFloat
(
f
:
Float
)
=
Writable
(
f
)
implicit def fromString
(
s
:
String
)
=
Writable
(
s
)
}
import Serialization
.
_
object RemoteConnection
{
//
➌
def write
[
T
<
%
Writable
](
t
:
T
):
Unit
=
//
➍
println
(
t
.
serialized
)
//
stdout
을
‘
원격
연결
’
로
사용한다.
}
RemoteConnection
.
write
(
100
)
//
출력
:
--
100
--
RemoteConnection
.
write
(
3
.
14f
)
//
출력
:
--
3
.
14
--
RemoteConnection
.
write
("
hello
!")
//
출력
:
--
hello
!
--
//
RemoteConnection
.
write
((
1
,
2
))
//
➎
➊ 단순화를 위해 문자열 (
String
)을 ‘이진’ 형식 대신 사용한다.
➋ 몇 가지 암시적 변환을 정의한다. 여기서는 메서드를 정의했지만, 우리에게 필요한 것은
A
=
>
B
타입의 함수다. 스칼라는 필요할 때 메서드를 함수로 끌어올린다는 사실을 기억하라.
➌ ‘원격’ 연결에 쓰는 것을 캡슐화한 객체
535
14
장
스칼라 타입 시스템 I
➍ 임의 타입의 인스턴스를 받아서 연결에 쓰는 메서드. 암시적 변환을 호출해서
serialized
메
서드를 호출한다.
➎ 튜플을 사용할 수 없다. 튜플에 대한 암시적 ‘뷰’가 없기 때문이다.
Predef
.
implictly
(
http
://
bit
.
ly
/
11QI0fF
)나 그와 비슷한 것이 필요하지 않다. 컴파일러가
우리를 위해 암시적인 변환을 호출해준다.
뷰 바운드가 더 멋지고 짧은 구문을 제공하지만, 뷰 바운드를 더 일반적인 맥락 바운드로 구현할
수도 있다. 따라서 스칼라 커뮤니티에서는 뷰 바운드를 사용 중단시키려는 논의가 진행되었다.
다음은 맥락 바운드를 사용해서 앞의 예제를 다시 쓴 것이다.
//
src
/
main
/
scala
/
progscala2
/
typesystem
/
bounds
/
view
-
to
-
context
-
bounds
.
sc
import scala
.
language
.
implicitConversions
object Serialization
{
case class Rem
[
A
](
value
:
A
)
{
def serialized
:
String
=
s
"
--
$
value
--
"
}
type Writable
[
A
]
=
A
=
>
Rem
[
A
]
//
➊
implicit val fromInt
:
Writable
[
Int
]
=
(
i
:
Int
)
=
>
Rem
(
i
)
implicit val fromFloat
:
Writable
[
Float
]
=
(
f
:
Float
)
=
>
Rem
(
f
)
implicit val fromString
:
Writable
[
String
]
=
(
s
:
String
)
=
>
Rem
(
s
)
}
import Serialization
.
_
object RemoteConnection
{
def write
[
T
:
Writable
](
t
:
T
):
Unit
=
//
➋
println
(
t
.
serialized
)
//
표준
출력을
‘
원격
연결
’
대신
사용
}
RemoteConnection
.
write
(
100
)
//
출력
:
--
100
--
//
➌
RemoteConnection
.
write
(
3
.
14f
)
//
출력
:
--
3
.
14
--
RemoteConnection
.
write
("
hello
!")
//
출력
:
--
hello
!
--
//
RemoteConnection
.
write
((
1
,
2
))
➊ 맥락 바운드를 더 쉽게 쓰기 위해 타입 별명을 사용하고, 앞의 예제에서 본 것과 상응하는
암시적 정의를 위치시킨다.
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.