Comment by continuational
12 hours ago
Sure, but this is an example from the article, and pertains to sum types in general, not just Maybe.
12 hours ago
Sure, but this is an example from the article, and pertains to sum types in general, not just Maybe.
i dont think its generally a good idea to be making complex type generators like this in zig. just write the type out.
the annoyingness of the thing you tried to do in zig is a feature. its a "don't do this, you will confuse the reader" signal. as for optional, its a pattern that is so common that it's worth having builtin optimizations, for example @sizeOf(*T) == @sizeOf(usize) but @sizeOf(?*T) != @sizeOf(?usize). if optional were a general sum type you wouldn't be able to make these optimizations easily without extra information
The point is that algebraic data types are common in functional languages. "Maybe" is just an example of an algebraic data type, there's tons more.
If the article says "functional programmers should take a look at Zig", and Zig makes algebraic data types hard, then maybe they shouldn't use it.
If you even say "the annoyingness is a feature, use zig the way it is intended to be used" then that's another signal for functional programmers that they won't be able to use zig the same way they use functional languages.
zig doesnt make algebraic types hard. algebraic types are exceptionally easy and also safe. and unlike rust, named in a way thats friendly to c devs.
zig makes stupid metaprogramming tricks on algebraic types annoying (not hard).
so, being precise: zig is not necessarily annoying for fp programmers (my main tool of trade in Elixir). zig is made to be annoying for architecture astronauts.
> if optional were a general sum type you wouldn't be able to make these optimizations easily without extra information
Rust has these optimizations (called "niche optimizations") for all sum types. If a type has any unused or invalid bit patterns, then those can be used for enum discriminants, e.g.:
- References cannot be null, so the zero value is a niche
- References must be aligned properly for the target type, so a reference to a type with alignment 4 has a niche in the bottom 2 bits
- bool only uses two values of the 256 in a byte, so the other 254 form a niche
There's limitations though, in that you still must be able to create and pass around pointers to values contained within enum, and so the representation of a type cannot change just because it's placed within an enum. So, for example, the following enum is one byte in size:
Variant A uses the valid bool values 0 and 1, whereas variant B uses some other bit pattern (maybe 2).
But this enum must be two bytes in size:
...because bool always has bit patterns 0 and 1, so it's not possible for an invalid value for A's fields to hold a valid value for B's fields.
You also can't stuff niches in padding bytes between struct fields, because code that operates on the struct is allowed to clobber the padding.
Yes, the care that Rust goes through to ensure that niches work properly, especially when composing arbitrary types from arbitrary sources, shows why you absolutely don't want to be implementing these optimizations by hand.