유니온 타입과 옵션 타입 (2025)

Union Types and Option Types (2025)

tibleiz.net ▲ 55 댓글 24 g0xA52A2A

요약

유니온 타입과 옵션 타입을 언어에 어떻게 넣을지 다룬 2025년 글입니다.

개인 사이트 tibleiz.net에 2025년 3월 올라온 글입니다. 서버가 응답하지 않아 원문을 읽지 못했습니다. HN 댓글이 인용한 구절을 보면, 글쓴이는 옵션 타입(값이 없을 수도 있음을 나타내는 타입)이 유니온 타입(여러 타입 가운데 하나를 담는 타입)보다 단순해 보여도 새 개념을 많이 요구해 언어를 복잡하게 만든다고 본 것으로 보입니다. 댓글들은 이 글을 Copper라는 프로그래밍 언어의 설계 설명으로 읽었습니다.

HN 반응

  • Copper가 구현 편의로 유니온의 중첩을 막고 멤버 순서에 의미를 둔 것을 두고, 기본 성질을 버렸다는 비판과 감수할 만하다는 반론이 갈렸습니다.
  • 태그(담긴 타입을 표시하는 값)를 값 뒤에 두자는 구현 요령에는 직렬화, 가변 크기 값, 캐시 미스를 들어 앞에 두는 편이 낫다는 반론이 나왔습니다.

댓글

23개 표시 · 전체 24개
  1. jakobnissen HN

    Copper가 유니온 타입을 넣어 놓고선, 구현상의 이유로 그 기본 기능은 물론 기본 속성까지 이렇게 많이 빼 버렸다니 저는 이해가 안 돼요.

    아니, 유니온 멤버를 세 개 못 쓴다는 게 무슨 말이에요? 유니온의 순서가 중요하다는 것도요. 이게 유니온 타입의 가장 쓸모 있는 속성들인데, Copper가 그렇게 한 이유라고 내놓은 논거도 영 빈약하고요.

  2. mrkeen HN

    유니온 멤버를 세 개 쓰는 건 가능해 보여요. 안 되는 건 중첩(이건 약점이네요)이랑 T1|T2와 T1|T2|T3 사이의 호환성(이건 아마 괜찮을 거예요)이고요. 순서가 문제라는 말은 저는 납득이 안 돼요.

  3. noelwelsh HN

    동감해요. 합 타입(sum type)과 유니온 타입의 차이를 이해하지 못하는 것 같아요. 예를 들어 태그가 걱정된다면 애초에 유니온 타입을 고려하면 안 되죠.

  4. gnabgib HN

    (2025) 그리고 CopperLang에 관한 글도 있었습니다 (203 points, 3일 전, 댓글 120개) https://news.ycombinator.com/item?id=50308754

  5. Panzerschrek HN

    옵션 타입은 유니온 타입보다 단순한데도 새로운 개념을 여럿 끌어들이고 추가 지식을 요구해서 언어를 더 복잡하게 만듭니다.

    그래서 저는 제 언어에서 이런 것들을 다른 방식으로 처리합니다. 옵셔널과 유니온 타입은 그냥 라이브러리의 컨테이너 클래스입니다. 필요하면 언어 코어는 건드리지 않고도 메서드를 더 추가하거나 내부 레이아웃을 바꿀 수 있습니다. 다만 이런 방식을 쓰려면 언어 자체가 그런 라이브러리 추상화를 만들 수 있는 수단을 제공해야 합니다.

  6. Panzerschrek HN
      var value = maybeValue.notNil // I know what I'm doing
    

    이건 정확히 어떻게 동작하나요? 런타임 검사를 하나요, 아니면 프로그래머를 그냥 믿나요?

  7. p1necone HN

    태그드 유니온을 구현할 때 쓰는, 단순하지만 (적어도 제게는) 눈에 잘 띄지 않던 요령이 하나 있습니다. 유니온 판별자/태그를 앞이 아니라 끝에 두는 겁니다. 그러면 유니온을 가리키는 포인터가 곧 값을 가리키는 포인터도 되기 때문에, 태그로 판별한 뒤에 태그 너머를 가리키도록 포인터를 옮길 필요가 없습니다.

  8. sparkie HN

    제 생각에는 좋지 않은 방법입니다.

    배열의 길이를 앞이 아니라 끝에 두는 것과 좀 비슷합니다. 오프셋을 알려면 먼저 크기를 알아야 하니까요.

    태그드 유니온의 값은 유연한 배열 멤버(flexible array member)처럼 태그에 따라 값의 크기가 달라질 수 있습니다. 인스턴스 대부분이 가장 작은 변형인데도 가능한 가장 큰 변형의 공간을 전부 할당해야 할 이유는 없습니다. 크기가 고정된 C union과 달리 태그드 유니온에는 그런 요구사항이 없어서, 태그에 따라 크기를 동적으로 정할 수 있습니다.

  9. p1necone HN

    보통 'Union(BigThing, SmallThing)' 같은 것을 다룰 때는 대부분의 자리에서 어차피 sizeof(BigThing + tag)만큼 할당해야 합니다. 구조체/배열 멤버가 되거나 스택에 놓이려면 크기를 정적으로 알아야 하니까요.

    '실제' 크기만 할당하고 넘어갈 수 있는 경우는, 그것을 별도의 메모리 할당에 넣고 앞서 말한 자리들에서는 포인터로 가리킬 때뿐입니다. BigThing이 '제정신인 구조체 크기 중 큰 편' 정도가 아니라 정말 크다면 그게 합리적인 선택이긴 합니다.

    분명히 해 두자면, 제가 말하는 것은 실제 메모리 레이아웃이 사용자 코드에는 불투명하고 비트 조작 최적화 때문에 모든 것이 이해하기 어려워지지 않는 언어 구현에서 하는 방식입니다. 손으로 짠 C 코드나 사람들이 메모리에 직접 접근하리라 기대하는 API에서 하는 방식이 아닙니다. 드문 일도 아닙니다. 제가 조사한 바로는 Swift가 그렇게 하고, 일부 C++ 표준 라이브러리 구현도 그렇게 합니다.

    (관련해서, Rust와 Swift, 그리고 아마 다른 언어들도 enum/유니온 레이아웃을 최적화하려고 쓰는 온갖 희한한 잔기술들도 꽤 흥미롭습니다. 많은 경우 태그 비트가 추가 공간을 전혀 차지하지 않거든요.)

  10. sparkie HN

    일부 언어(예: 닷넷)에서는 태그드 유니온을 OOP 계층 구조로 구현합니다. 예를 들면 이런 식입니다.

        public sealed abstract class Option<T> {
            private Option() { }
            public sealed class Some<T> : Option<T> {
                private readonly T value;
                public Some(T value) {
                    this.value = value;
                }
            }
            public sealed class None<T> : Option<T> {
                public None() { }
            }
            public static Option<T> Some(T value) { return new Some(T); }
            static readonly Option<T> _None = new None();
            public static Option<T> None() { return _None; }
        }
    

    여기서 Option.Some이나 Option.None을 쓸 수 있고, 반환 타입은 매개변수에서 추론됩니다.

    None의 sizeof는 Some<T>의 sizeof와 같지 않습니다. Some 클래스에는 None 클래스에 없는 필드가 하나 더 있고, Some의 크기는 타입 매개변수 T에 따라 달라지니까요.

    객체에 RTTI가 있다면 태그 필드조차 필요 없습니다. 객체 안의 타입 정보가 암묵적인 태그 구실을 하니까요. 패턴 매칭 기능으로 opt is Some<T>나 opt is None을 검사하면 됩니다.

    C에서 같은 일을 하려면 옵션을 가리키는 포인터를 써야 합니다. 예를 들어 GCC 확장 몇 가지를 쓰면 이렇게 할 수 있습니다.

        enum option_tag { OPT_NONE, OPT_SOME };
        typedef struct option { enum_option_tag tag; } *Option;
        struct none { struct option base; } _none = { .base.tag = OPT_NONE };
        Option None = (Option)&_none;
    
    #define Some(T, _value) \
            ({ \
                struct some_##T { \
                    struct option base; \
                    T value; \
                } *result = GC_alloc(sizeof(struct some_##T)); \
                result->base.tag = OPT_SOME; \
                result->value = (_value); \
                (struct option*)result; \
            })
    
    bool is_some(Option opt) { 
            return opt->tag == OPT_SOME;
        }
        bool is_none(Option opt) {
            return opt->tag == OPT_NONE;
        }
    
    #define _get_some(T, opt) (((struct some_##T*)(opt))->value)
    

    이 방식은 태그가 첫 번째 필드일 때만 동작합니다. 첫 필드가 같지 않은 서로 다른 타입의 포인터끼리 캐스팅하는 것은 UB이기 때문입니다.

    C에서 Option을 구현하는 가장 효율적인 방법은 아니지만, 다른 태그드 유니온에서는 값 중에 유난히 큰 것이 있다면 이 방식이 이득일 수 있습니다.

  11. adrian_b HN

    유니온의 흔한 구현 방식 중에는 포인터를 옮길 필요가 없는 것이 있습니다. 태그를 음수 오프셋으로 접근하고, 포인터는 항상 값을 가리키기 때문입니다.

  12. beached_whale HN

    그래도 이런 것을 직렬화할 때는 대개 태그를 앞에 두는 것이 가장 좋습니다. 그래야 어떻게 파싱할지 알아내는 비용이 적게 듭니다.

  13. vrotaru HN

    직렬화 형식이 메모리 내 표현 형식과 같을 필요는 없습니다.

    타이트 루프에서 역직렬화와 직렬화를 하는 경우가 아니라면 그 정도는 받아들일 만하다고 생각합니다.

    참고로 더 큰 문제는, 넘겨받은 것이 여러 종류 중 어느 것인지 런타임에 정해야 할 때입니다.

  14. Panzerschrek HN

    현대 컴퓨터에서 포인터 연산은 사실상 공짜입니다. 하지만 멀리 떨어진 곳에 있는 유니온 태그에 접근하다가 생기는 캐시 미스는 공짜가 아닙니다.

  15. ks2048 HN

    언어 기능을 여러 언어에 걸쳐 설명해 놓은 가장 좋은 출처가 뭘까요? 예를 들면 “옵셔널 타입”은 어떤 언어에 있고 어떻게 동작하는지 같은 거요. “유니온 타입” 등도 마찬가지고요.

  16. DonaldPShimoda HN

    안타깝게도 이런 것을 다 알려 주는 단일한 기준은 없습니다. 흔히 인용되는 것으로 벤저민 피어스(Benjamin Pierce)의 "Types and Programming Languages"(2002)가 있지만, 그 밖에는 대개 각자 특정 언어의 용어를 쓰면서 언어를 넘나드는 모험가들이 알아서 이해해 주길 바랄 뿐입니다. 다만 최근 언어일수록 편차가 좀 적은 편이라, 어떤 면에서는 예전만큼 큰 문제는 아니라고 봅니다.

  17. tylermk04 HN

    Rosetta Code가 도움이 될 때가 많아요. 꼭 언어 기능 얘기만 있는 건 아니지만요. https://rosettacode.org/wiki/Monads/Maybe_monad

  18. cardboardguru HN

    Swift는 이미 이 모든 질문에 답을 내놨죠.

  19. direwolf20 HN

    저는 Onion Types(양파 타입)로 읽었어요

  20. budro HN

    글쎄요, 설계가 형편없으면 옵셔널을 여러 겹 벗겨 내야 할 수도 있잖아요. 그래서 양파처럼 옵셔널도 흔히 상당한 악취(code smell)의 징후거든요.

  21. andyferris HN

    슈렉이라면 겹겹이 있는 건 버그가 아니라 기능이라고 우기겠네요.

  22. smnrchrds HN

    프로그래밍에 대한 필로미나 컹크(Philomena Cunk)

    https://www.youtube.com/shorts/XK_D7Z-cKZ4

  23. SoleilAbsolu HN

    그렇게 샬롯(shallot) 될지어다...

Hacker News에서 보기 ↗