You would have to keep everything in variants, or wrap/unwrap manually all over the place to get similar functionality.
And C has tagged unions.
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.
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.
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.
Haha, that's like saying that C has templates because it has the _Generic macro.
>> We chose Rust because:
>> It has a very minimal runtime (essentially just bounds checks on arrays) and is easy to embed into other languages such as Java and Python
>> It contains functional programming paradigms such as pattern matching that make writing code such as pattern matching compiler optimizations easier
>> It has a great community and high quality packages (called “crates” in Rust) that made developing our system easier.
They could've used an ML with GC and it would've been better (for a compiler).
It doesn't really have any functional programming paradigms. Pattern matching is present in imperative languages like past versions of ATS
No, they originally wrote the runtime in C++, but ended up re-implementing it in Rust because the C++ runtime had too many bugs.
> They could've used an ML with GC and it would've been better (for a compiler).
They originally wrote the compiler in Scala with the JVM GC, and they said it was much slower and much harder to embed.
> It doesn't really have any functional programming paradigms. Pattern matching is present in imperative languages like past versions of ATS
When choosing a language for such a project, there are many engineering trade-offs that must be evaluated.
The Weld project has dozens of developers that need onboarding, documentation, examples, tooling, etc. Rust has a lower barrier of entry than ATS. One of the main things Weld does is interfacing with LLVM: this is one of the main things the Rust compiler does and Rust has great libraries for this. Another thing Weld does is interfacing with many dynamic languages (Python, R, etc.). Rust does not just have "C FFI". It also has _a lot_ of great tooling for automatically generating and validating all the boilerplate automatically. Finally, performance and code size of Weld is one of the main advantages over the alternatives. Rust generates reasonable code with LLVM, ATS has its own code machine code generator, which while reasonable, isn't as good.
Finally, it is hard to find people who enjoy writing ATS code. They exist, but are not many. Even when you do find them, they often don't like collaborating with people (I only know one ATS user, vmchale on github, and they don't really like interacting with others). OTOH it is trivial to find lots of people that enjoy writing Rust with others. It doesn't matter if this is due to technical reasons, marketing, or hype, but it's a fact that you have to consider if you want a project to grow fast.
I don't really know why you'd write a project of this sort in C.