Hacker News new | ask | show | jobs
by fluffything 2456 days ago
That would be like explaining C++ Concepts to an assembly programmer from the 60s that had never used a "function" as a way of abstracting code.

If you really want to know, spend one afternoon learning any programming language with built in support for that (Rust, Ocaml, Haskell, ...). ADTs is one of the first things one learns.

In Rust, the features you'd need to learn are enums, patterns, and pattern matching.

But be warned that using C++ variant and std::visit will feel like you are being forced to only write C instead of C++ for the rest of your life, knowing that life could be much better. Once you learn this, there is no way to un-learn it.

1 comments

I know how these things work in Rust, and I'm still failing to see your point. It's not at all as complicated as what you are saying given that you can find many blog articles that explain it succinctly in a couple paragraphs.

It's not at all helpful to say that there's something so complicated on these other languages that you can't possibly get the idea across without using them.

Try doing any of this with variant and visit

    enum A { Foo{ x: i32, y: f32 }, Bar(B), Baz([u32; 4]), Moo(i32), Mooz(i32) }
    struct B { z: f32, w: (f64, f32) }

    let b = B { z: 42.0, ..}; // create a b with z == 42 and default w
    let B{ w: (first, _), .. } = b; // get B.w.0 field
    let a = A::Foo{ x: 42.0, ..};
    if let A::Bar(Bar { z, ..}) = a {
      // if a is an A::Bar, get b.z field of A::Bar(b)
    }
    if let A::Baz([0, 1, 2, 3]) = a {
      // if a is an A::Baz containing an array 
      // with value [0, 1, 2, 3]
    }
    match a {
        A::Moo(1..3 @ v) => {
           // if a is an A::Moo where x in A::Moo(x) is in range [0, 3) and put the value in the local v variable
        }
        A::Moo(x) | A::Mooz(x) => { 
           // Either A::Moo or A::mooz, gives you the value of x
        }
        // ERROR: I forgot to match some patterns
    }

    foo(b);

    fn foo(B{ z, ..}: B) -> f64 { 
      // get the z field of the first function argument
      z
    }
What in Rust are one liners, and can be used anywhere (let bindings, constructors, match, if-let, while-let, function arguments, ...) is a pain to write in C++ using `std::visit` and `std::variant`. The error messages of `std::visit` + `std::variant` are quite bad as well. And well, then there are also other fundamental problems with variant like `variant<int>` having two variants, `variant<int, int>` having 3 variants, but you can't reach the second `int`, etc.

You can translate all the code above to C++ to use std::visit + std::variant instead. I personally find that C++ is unusable for programming like this, and almost never use std::variant in C++ as a consequence, while I use ADTs in Rust all the time.

One difference is that std::variant is run-time dispatched and uses memory equal to the max of all the variants, while Rust's sum types could potentially be compile-time dispatched and memory optimized to the exact type being used through various code paths.