3 ms·
If you've never used a language with good support for sum types, they sound worthless. That's definitely my experience, I never used a language featuring sum t
by simias 12y ago
If you've never used a language with good support for sum types, they sound worthless.
That's definitely my experience, I never used a language featuring sum types before I started learning rust a few months ago but now I miss them as soon as I go back to C.
For those of you who are not yet enlightened here's a couple examples: I had to implement a time counter struct. It was either counting, in which case I would only store the date corresponding to the time 0 (and I would compute the current counter value by substracting the current time to this date instead of having to update the counter every second) or halted in which case I would just store the last counter value.
So my first try was:
struct Counter {
counting: bool,
/// When counting is true
tzero: u32,
/// When counting is false
last_count: u32,
}
Now of course, that's error prone and wasteful. Wasteful because you store two u32 when only one of them will be used at any given time and error prone because you have to be careful to always correctly check the value of counting before using one of the other values. You could save some space by using a single "tzero_or_last_count" u32 but that makes it even more error prone.
And then I saw the light:
enum Counter {
Counting(u32),
Halted(u32),
}
This does exactly what I needed: No wasted space since this only takes the space for a single u32 and then a little more for the tag, the equivalent of my counting flag above but handled by the language directly. And it's so much nicer to handle: the language won't let me access the underlying integers unless I match the enum which forces me to check. What was an implicit convention left for the coders to enforce is now checked by the compiler.
An other example I have is the following:
enum CacheState {
Valid(Cache),
Dirty,
}
I can only access the Cache when it's not Dirty. In C or C++ you could get similar results by using encapsulation and have accessor functions that check the dirty flag but you have to implement and debug all that by yourself. With sum types it's built directly into the language.
- inglor 12y agoThat `enum Counter` example (and Either in general) is a union type and not a sum type. It's either Counting (with a u32) or Halted (with a u32) but never both at the same time.
- Cyther606 12y agoAs another illustrative example, let's say we want to create a library for working with mathematical expressions such as x^2 + 5*x. It's natural to define our data types like this: type FormulaKind = enum fkVar, ## element is a variable like 'X' fkLit, ## element is a literal like 0.1 fkAdd, ## element is an addition operation fkMul, ## element is a multiplication operation fkExp ## element is an exponentiation operation type Formula = ref object case kind: FormulaKind of fkVar: name: string of fkLit: value: float of fkAdd, fkMul, fkExp: left, right: Formula Nim's enum [1] is an old-school typesafe enum, as in Ada, without any fields. To avoid name clashes, it's common to prefix the enum values with a two-letter abbreviation. The case part in the object declaration introduces a checked union. So the access of f.name will raise an exception if f.kind != fkVar. [1] http://goran.krampe.se/2014/10/20/i-missed-nim/ http://goran.krampe.se/2014/10/20/i-missed-nim/