합집합 타입

합집합 타입 (Union Types)

타입에 | 연산자를 쓰면 **합집합 타입(union type)**이 만들어져요. A | B 타입은 타입 A이거나 타입 B인 값을 나타내죠.

type Hash = Int
def lookupName(name: String) = ???
def lookupPassword(hash: Hash) = ???
trait ID
case class UserName(name: String) extends ID
case class Password(hash: Hash) extends ID

def help(id: UserName | Password) =
  val user = id match
    case UserName(name) => lookupName(name)
    case Password(hash) => lookupPassword(hash)
  /// ... omitted

출처: Scala 3 Reference

본문

합집합 타입은 교집합 타입(intersection type)의 쌍대(dual)예요. 그리고 |교환 법칙이 성립해요. A | BB | A와 같은 타입이죠.

컴파일러가 표현식에 합집합 타입을 부여하는 경우는 두 가지뿐이에요. 그런 타입이 명시적으로 주어졌을 때, 또는 모든 대안들의 공통 슈퍼타입이 transparent일 때예요.

이건 다음 snippet 기록에서 확인할 수 있어요:

type Hash = Int
def lookupName(name: String) = ???
def lookupPassword(hash: Hash) = ???
trait ID
case class UserName(name: String) extends ID
case class Password(hash: Hash) extends ID

def help(id: UserName | Password) =
  val user = id match
    case UserName(name) => lookupName(name)
    case Password(hash) => lookupPassword(hash)
  /// ... omitted
val password = Password(123)
// password: Password = Password(123)

val name = UserName("Eve")
// name: UserName = UserName(Eve)

if true then name else password
// _ : ID = UserName(Eve)

val either: Password | UserName = if true then name else password
// either: UserName | Password = UserName(Eve)

res1의 타입은 ID인데, 이는 UserNamePassword의 슈퍼타입이지만 최소 슈퍼타입(least supertype)인 UserName | Password는 아니에요. 최소 슈퍼타입을 얻고 싶다면 either의 타입에서처럼 명시적으로 주어야 해요.

공통 슈퍼트레이트 IDtransparent로 선언되면 추론 동작이 달라져요:

type Hash = Int
transparent trait ID
case class UserName(name: String) extends ID
case class Password(hash: Hash) extends ID

이 경우에는 합집합 타입이 넓혀지지 않아요:

type Hash = Int
transparent trait ID
case class UserName(name: String) extends ID
case class Password(hash: Hash) extends ID
val (name, password) = (UserName("Eve"), Password(123))
if true then name else password
// _: UserName | Password = UserName(Eve)

더 정밀한 합집합 타입은 UserNamePassword가 명시적인 부모 없이 선언될 때도 추론돼요. 그런 경우에 이들이 암시적으로 상속하는 슈퍼클래스가 Object인데, Object는 transparent로 간주되는 클래스들에 포함되거든요. 그런 클래스들의 목록은 Transparent Traits and Classes 문서에서 확인할 수 있어요.

type Hash = Int
case class UserName(name: String)
case class Password(hash: Hash)

if true then UserName("Eve") else Password(123)
// _: UserName | Password = UserName(Eve)