공용체 타입

한 변수나 표현식은 다수의 타입을 가질 수 있습니다. 이런 경우를 공용체 타입을 갖는다고 표현합니다. 한 변수를 if 분기문 안에서 다른 값을 할당할 때를 예로 들 수 있습니다.

if 1 + 2 == 3
a = 1
else
a = "hello"
end

a # : Int32 | String

분기문 끝에서 aInt32 | String 타입을 가지며, 이는 "Int32와 String의 공용체"입니다. 이 공용체 타입은 컴파일러에 의해 자동으로 생성됩니다. 실행 시간에 a는 물론 그 중 한 타입만을 갖습니다. 이는 class 메서드를 호출하여 볼 수 있습니다.

# 실행 시간 타입
a.class # => Int32

컴파일 시간 타입은 typeof으로 볼 수 있습니다.

# 컴파일 시간 타입
typeof(a) # => Int32 | String

공용체 타입은 아주 많은 수의 타입으로 이루어질 수 있습니다. 공용체 타입을 갖는 변수에 메서드를 호출할 때, 공용체 타입의 모든 타입이 해당 메서드를 갖지 않는다면 컴파일 시간 오류가 발생합니다. 반환 값의 타입은 그 메서드들의 반환 타입의 공용체 타입입니다.

# to_s는 Int32와 String에 정의되어 있으며 String을 반환
a.to_s # => String

a + 1 # String#+(Int32)는 정의되어 있지 않기 때문에 오류 발생

필요한 경우라면 컴파일 시간에 변수를 공용체 타입으로 정의할 수도 있습니다.

# 컴파일 시간 타입 지정
a = 0.as(Int32|Nil|String)
typeof(a) # => Int32 | Nil | String

공용체 타입 규칙

T1T2의 두 타입이 공용체가 될 때 결과는 보통 T1 | T2가 됩니다. 하지만 결과 타입이 다른 경우도 있습니다.

같은 위계를 갖는 클래스와 구조체의 공용체

T1T2가 같은 위계 구조를 가지며 가장 가까운 공통 조상이 Reference, Struct, Int, Float, Value가 아닌 Parent일 경우 결과 타입은 Parent+가 됩니다. 이를 가상 타입이라고 합니다. 컴파일러는 이 타입을 Parent 또는 그 타입을 상속하는 타입으로 취급할 것입니다.

class Foo
end

class Bar < Foo
end

class Baz < Foo
end

bar = Bar.new
baz = Baz.new

# 이때 foo의 타입은 Bar | Baz이지만,
# Bar과 Baz가 Foo를 상속하기 때문에
# 결과 타입은 Foo+
foo = rand < 0.5 ? bar : baz
typeof(foo) # => Foo+

같은 크기를 갖는 튜플의 공용체

같은 크기를 갖는 두 튜플의 공용체는 각 위치에 주어진 타입들의 공용체 타입을 갖는 튜플 타입이 됩니다.

예를 들자면, 다음과 같습니다.

t1 = {1, "hi"} # Tuple(Int32, String)
t2 = {true, nil} # Tuple(Bool, Nil)

t3 = rand < 0.5 ? t1 : t2
typeof(t3) # Tuple(Int32 | Bool, String | Nil)

같은 키를 갖는 이름 달린 튜플의 공용체

두 개 이상의 이름 달린 튜플이 (순서와 상관 없이) 같은 키를 갖는다면 각 키의 공용체 타입을 갖는 이름 달린 튜플이 됩니다. 이 키의 순서는 좌측에 있는 것의 순서를 따릅니다.

t1 = {x: 1, y: "안뇽"} # Tuple(x: Int32, y: String)
t2 = {y: true, x: nil} # Tuple(y: Bool, x: Nil)

t3 = rand < 0.5 ? t1 : t2
typeof(t3) # NamedTuple(x: Int32 | Nil, y: String | Bool)

results matching ""

    No results matching ""