NHacker Next
  • new
  • past
  • show
  • ask
  • show
  • jobs
  • submit
▲Union Types and Option Types (2025) (tibleiz.net)
jakobnissen 13 hours ago [-]
It seems bizarre to me that Copper includes Union types and then, for implementation reasons, does not include so many of its basic features, and even its basic properties.

Like, what do you mean you can’t have three union members? And that the order of the union matters? These are some of the most useful properties of union types, and the arguments for what Copper did are weak sauce.

mrkeen 8 hours ago [-]
It looks like you can have three union members, just not nesting (weak) or compatibility between T1|T2 and T1|T2|T3 (probably fine). I'm not convinced about ordering being a problem.
noelwelsh 11 hours ago [-]
Agreed. They don't seem to understand the difference between sum types and union types. If you're worried about tags you shouldn't be considering union types in the first place, for example.
gnabgib 2 days ago [-]
(2025) And about CopperLang (203 points, 3 days ago, 120 comments) https://news.ycombinator.com/item?id=50308754
Panzerschrek 11 hours ago [-]
> The option type, despite being simpler than union type introduces many new concepts and requires additional knowledge that make the language more complex.

That's why I do such things in my language in a different way. Optional and union types are just library container classes. More methods may be added if necessary, internal layout may be changed, etc. without changing the language core itself. But in such an approach the language itself should provide necessary means allowing creation of such library abstractions.

cardboardguru 16 hours ago [-]
Swift has already answered all these questions.
Panzerschrek 11 hours ago [-]

  var value = maybeValue.notNil // I know what I'm doing
How does it work exactly? Does it perform a runtime check, or just trusts the programmer?
p1necone 19 hours ago [-]
One simple but non obvious (at least to me) trick when implementing tagged unions: put the union discriminator/tag at the end, not the beginning - that way a pointer to a union is also a pointer to the value, no need to increment the pointer to point past the tag after discriminating.
sparkie 12 hours ago [-]
IMO, a bad idea.

It's a bit like putting the length of an array at the end rather than the beginning though - you need to know its size first to know the offset.

The value of a tagged union may be like a flexible array member - in that the size of the value depends on the tag. We shouldn't need to allocate the maximum possible variant's space when most of the instances may be the smallest. Unlike a C `union`, which are a fixed size, a tagged union has no such requirement - it can be dynamically sized depending on tag.

p1necone 11 hours ago [-]
Usually when dealing with something like 'Union(BigThing, SmallThing)' you have to allocate sizeof(BigThing + tag) in most places anyway because you need to statically know the size so that it can be a struct/array member, live on the stack etc.

The only time you can get away with only allocating the 'real' size is if you're sticking it in its own separte memory allocation and using a pointer to refer to it in the aforementioned contexts. Which, to be fair, is a sensible thing to do if BigThing is very big, as opposed to just 'on the bigger end of sane struct sizes'.

To clarify I'm talking about what you'd do in a language implementation where the actual memory layout is opaque to userspace and bit twiddling optimizations aren't making things harder to understand for everything, not what you'd do when writing handwritten C code or an api where you expect people to access the memory directly. This isn't unheard of - e.g. from my research Swift does it, some C++ stdlib implementations do it.

(Related - the rest of the bag of weird tricks that Rust and Swift and presumably other languages use to optimize enum/union layout are pretty interesting - the tag bits actually take up no extra space in many cases).

sparkie 1 hours ago [-]
In some languages (eg, on dotnet), tagged unions are implemented through an OOP hierarchy, where you have, for example, something like:

    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; }
    }
Where you can use `Option.Some` or `Option.None`, and the return type is inferred from the parameter.

The sizeof(None) is not the same as sizeof(Some<T>) - the Some class has an additional field not present in the None class - and the size of Some depends on the type parameter T.

If the objects have RTTI, we don't even need a tag field, as there is an implicit tag in the form of type information in the object - the pattern matching facility can test `opt is Some<T>` or `opt is None`.

In C we would have to use a pointer to option to do the equivalent, eg with some GCC extensions we can do:

    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)
This ONLY works if the tag is the first field - casting between pointers to different types is UB if the first fields are not the same.

Not the most efficient way to implement an Option in C, but for other tagged unions, if any of the values are particularly large this may be beneficial.

adrian_b 7 hours ago [-]
A frequent implementation method for unions does not need to change the pointer, because the tag is accessed with a negative offset and the pointer always points to the value.
beached_whale 18 hours ago [-]
when serializing these things it's generally going to be best to put the tag first though. That way it's cheap to figure out how to parse it.
vrotaru 12 hours ago [-]
The serialization format, does not need to be the in-memory representation format.

Unless, you do de-serialization and serialization in tight-loop I guess is it acceptable.

NOTE: A bigger problem is when you need to decide at runtime which one of the kind you have been handled.

Panzerschrek 10 hours ago [-]
Pointer arithmetic on modern computers is basically free. But cache-misses caused by accessing a union tag located far away are not free.
ks2048 18 hours ago [-]
What is the best source for cross-language description of language features? Eg: “optional types”: which langs have them and how do they work? Same with “union types” etc.
DonaldPShimoda 17 hours ago [-]
There's no single oracle for these things, unfortunately. One common citation is "Types and Programming Languages" by Benjamin Pierce (2002), but otherwise people generally just use the nomenclature of a specific language and hope cross-linguistic adventurers will figure it out. I think there's somewhat less variance with more modern languages, though, so in some ways it's less of an issue than it once was.
tylermk04 18 hours ago [-]
Rosetta code is often helpful. Not exactly for language features, necessarily: https://rosettacode.org/wiki/Monads/Maybe_monad
direwolf20 19 hours ago [-]
i read Onion Types
budro 18 hours ago [-]
Well poor design can require you to peel off multiple layers of optionals, and so they're often a significant smell just like onions are.
andyferris 18 hours ago [-]
Shrek would argue the layers are a feature not a bug
smnrchrds 17 hours ago [-]
Philomena Cunk on Programming

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

SoleilAbsolu 19 hours ago [-]
So shallot be...
speed_spread 16 hours ago [-]
No matter how you cut them, they'll make you cry.
yksalun 12 hours ago [-]
[flagged]
20 hours ago [-]
Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact
Rendered at 19:22:20 GMT+0000 (UTC) with Wasmer Edge.