튜플 함수
튜플 함수 (Tupled Function)
함수의 인자(argument)를 하나의 튜플로 묶어서 다루고 싶을 때가 있어요. TupledFunction 타입 클래스는 임의의 애리티(arity)를 가진 함수와, 그 함수의 모든 인자를 하나의 튜플로 받는 동등한 함수 사이를 추상화하는 방법을 제공해요.
본문
스칼라에서 함수의 애리티가 22까지로 제한되어 있을 때는 오버로딩으로 모든 함수 타입에 공통된 연산을 일반화하는 게 가능했어요. 하지만 함수와 튜플이 22를 넘는 애리티까지 일반화된 지금은, 오버로딩만으로는 이걸 처리할 수 없게 됐죠. TupledFunction 타입 클래스가 바로 이 문제를 해결해 줘요. 어떤 애리티의 함수든 직접 추상화해서, 그 함수의 모든 인자를 하나의 튜플로 받는 동등한 함수로 바꿔주는 거예요.
이 타입 클래스는 다음과 같이 정의돼 있어요.
/** Type class relating a `FunctionN[..., R]` with an equivalent tupled function `Function1[TupleN[...], R]`
*
* @tparam F a function type
* @tparam G a tupled function type (function of arity 1 receiving a tuple as argument)
*/
@implicitNotFound("${F} cannot be tupled as ${G}")
sealed trait TupledFunction[F, G] {
def tupled(f: F): G
def untupled(g: G): F
}
컴파일러는 다음 조건이 모두 맞을 때 TupledFunction[F, G]의 인스턴스를 자동으로 합성해요.
F가 애리티N의 함수 타입이어요.G가 크기N의 튜플 하나를 받는 함수이고, 그 타입이F의 인자 타입들과 같아요.F의 반환 타입이G의 반환 타입과 같아요.F와G가 같은 종류의 함수예요. (둘 다(...) => R이거나 둘 다(...) ?=> R)F와G중 하나만 인스턴스화했다면, 나머지 하나는 추론돼요.
예시 (Examples)
TupledFunction을 쓰면 Function1.tupled, ..., Function22.tupled 메서드를 임의의 애리티를 가진 함수로 일반화할 수 있어요. 아래는 tupled를 확장 메서드로 정의한 전체 예시예요.
/** Creates a tupled version of this function: instead of N arguments,
* it accepts a single [[scala.Tuple]] with N elements as argument.
*
* @tparam F the function type
* @tparam Args the tuple type with the same types as the function arguments of F
* @tparam R the return type of F
*/
extension [F, Args <: Tuple, R](f: F)
def tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f)
TupledFunction을 쓰면 Function.untupled도 임의의 애리티를 가진 함수로 일반화할 수 있어요. (전체 예시)
/** Creates an untupled version of this function: instead of a single argument of type [[scala.Tuple]] with N elements,
* it accepts N arguments.
*
* This is a generalization of [[scala.Function.untupled]] that work on functions of any arity
*
* @tparam F the function type
* @tparam Args the tuple type with the same types as the function arguments of F
* @tparam R the return type of F
*/
extension [F, Args <: Tuple, R](f: Args => R)
def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f)
TupledFunction은 Tuple1.compose와 Tuple1.andThen 메서드를 일반화해서, 더 큰 애리티의 함수나 튜플을 반환하는 함수를 합성하는 데도 쓸 수 있어요.
/** Composes two instances of TupledFunction into a new TupledFunction, with this function applied last.
*
* @tparam F a function type
* @tparam G a function type
* @tparam FArgs the tuple type with the same types as the function arguments of F and return type of G
* @tparam GArgs the tuple type with the same types as the function arguments of G
* @tparam R the return type of F
*/
extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F)
def compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = {
(x: GArgs) => tf.tupled(f)(tg.tupled(g)(x))
}