Hacker News new | ask | show | jobs
by kraghen 3204 days ago
The C++17 equivalent would be something like the following (not tested):

  using NumberExpr = int;
  using VarExpr = std::string;
  struct AddExpr;

  using Expr = std::variant<NumberExpr, AddExpr, VarExpr>;

  struct AddExpr {
    std::unique_ptr<Expr> a;
    std::unique_ptr<Expr> b;
  }
Of course, this being C++, you need forward declarations and a firm grasp of the rules of incomplete types to be confident about declaring a simple AST type.
1 comments

To completely address JoshTriplett's point, yes, you can just define another struct for the SubExpr variant to disambiguate it from the AddExpr case.

Requiring this kind of wrapping is awkward compared to e.g. Rust or Haskell's treatment of sum types, which unlike C++17 and std::visit both have powerful pattern matching features built into the language. Saying this as someone who writes C++ all day: std::visit and std::variant are weaksauce.

On the other hand, the C++ way gives you an actual type for each element of the sum; you can write a function which only takes AddExpr. The Rust way doesn't (yet).
Given that you have to define those types manually, I don't see why you couldn't do the same in Rust; it just doesn't force you to if you don't need it
That's rather disingenuous, because Haskell forces you to use `newtype` wrappers for lots of things you shouldn't need them for and don't need them for in C++.
Can you name an instance in Haskell where newtype is conceptually unnecessary but required by the language? In the sense that you may be able to derive the same set of logical guarantees that newtype gets you without using it, in principle.