Hacker News new | ask | show | jobs
by mgsloan2 1189 days ago
> if your type-system is sufficently strong to express this

No fanciness needed, just plain old sum types. It is certainly possible to express those invariants directly in languages with a dependent type systems or refinement types like in liquid haskell - see https://ucsd-progsys.github.io/liquidhaskell-tutorial/Tutori.... It's typically much easier to reason about and use sum types, though.

Of course these examples are trivial and silly, but I see instances of these patterns all the time in big co software, and of course usually the invariants are far more complex but many could be expressed via sum types. I've seen loads of bugs from constructing data that invalidates assumptions made elsewhere that could have been prevented by sum types, as well as lots of confusion among engineers about which states some data can have.

> Field X is only set if field Y is true

Original gnarly C style pattern:

    struct TurboEncabulatorConfig {
        // When true, the turbo-encabulator must reticulate splines, and 'splines'
        // must be non-null. When false, 'splines' must be null.
        bool reticulate_splines;
        struct Splines *splines;
    };
Rust (let's ignore pointer vs value distinction):

    enum TurboEncabulatorConfig {
        NonReticulatingConfig,
        ReticulatingConfig { splines: Splines },
    }
> If field X is non-null then field Y must be null and vice-versa.

Original gnarly C style pattern:

    struct TurboEncabulatorConfig {
        // When non-null, lunar_waneshaft must be null. 
        struct Fan *pentametric_fan;
        // When non-null, pentametric_fan must be null.
        struct Shaft *lunar_waneshaft;
    };
Rust:

    enum TurboEncabulatorConfig {
        PentametricTurboEncabulator { pentametric_fan: Fan },
        LunarTurboEncabulator { lunar_waneshaft: Shaft },
    }