Live data from Hacker News

Algebraic Data Types for C99

github.com

161–170 of 233 posts

Re: Algebraic Data Types for C99

#161

Earlier quoted context omitted.

Pascal has had variant records since the 1970s.

But Pascal's variant records (1970-11) had very ugly design errors in comparison with the unions of Algol 68 (1968-12), which made them either useless or annoying for most applicatons. Niklaus Wirth is well known as a critic of Algol 68 (before the design of Algol 68 was finalized), but in the case of his variant records he has completely failed to create something competitive.

Doesn't look too bad particually the match tagged version last:

  MyType= (Scalar4, Real4, NullTerminatedStringC);
  
  MyUntaggedRecType=
      RECORD
      CASE MyType OF
      Scalar4: (longC:  ARRAY[1..150] OF longint);
      Real4:   (floatC: ARRAY[1..150] OF real);
      END;
  
  MyTaggedRecType=
      RECORD
      CASE tag: MyType OF
      Scalar4: (longC:  ARRAY[1..150] OF longint);
      Real4:   (floatC: ARRAY[1..150] OF real);
      END;
  
  ...
  
  { set all to 0.0 without running through the MC68881 }
  FOR j := 1 TO 150 DO
      longC[j]:= 0;
  
  ...
  
  CASE tag OF
      Scalar4: percentReal = longC[1];
      floatC:  percentReal = floatC[1]*100;
  ELSE
      percentReal = 0.0/0.0;
edit: don't have a pascal compiler handy, but that's the idea

Re: Algebraic Data Types for C99

#162
post #155

> PLEASE, do not use top-level break/continue inside statements provided to of and ifLet; use goto labels instead. Seems like a pretty big footgun. But otherwise, very cool.

goto is a footgun only if you use it to move from function to function, which btw was what "goto considered harmful" was about. That practice has disappeared, and now goto, within a function, is pretty harmless and quite identical to break/continue in fact.

Goto isn't the footgun. The footgun is if you use break/continue by accident then some unspecified bad thing will happen, silently I'm guessing.

Re: Algebraic Data Types for C99

#163
post #157

Earlier quoted context omitted.

Everyone who hasn't used ADTs and pattern matching doesn't get what the big deal is all about. Everyone who is used to ADTs and pattern matching doesn't get what the big deal is all about, until they have to work in a language that doesn't have them. And everyone who just found out about them can't shut up about them being the best thing since sliced bread. :)

I’m in the latter camp (from Ocaml) and now using Go. Go feels clunky and awkward.

That's because Go is intentionally clunky and awkward in the name of "simplicity". IMO it's charming to some degree, but it's far from perfect and I think you'd need some pretty serious threats to get me to describe it as "elegant" in any way.

Rust somehow has more elegance than Go, if only in small parts. Nothing compares to Scheme in the elegance category IMO :)

Re: Algebraic Data Types for C99

#164

Earlier quoted context omitted.

> Could you not get most of the benefits of ADTs using structs + unions + enums? The modelling aspects can be simulated, yes, but that's barely half of the benefits of ADTs. Pattern matching is a big ergonomic benefit.

TFA gets pattern matching. The critical thing is that the compiler (or macro system) needs to check that you've checked all the alternatives.

Yes TFA is pretty close, but note that it's not just "structs + unions + enums" getting you "most of the benefits", which is what I was responding to. There's a buttload of macros hiding allocation and switch statements.

Re: Algebraic Data Types for C99

#165

Earlier quoted context omitted.

In Rust [0]: #[derive(Debug)] pub enum Example { Foo(i32), Bar(&'static str), } let mut ex: Example = Example::Foo(42); println!("{ex:?}"); // Foo(42) let ex_ref: &mut Example = &mut ex; *ex_ref = Example::Bar("hello"); println!("{ex:?}"); // Bar("hello") Given a mutable reference to a value of enum type, you can replace it with another variant. Or you can swap it out with any other value of the same type, even if th…

This doesn't have anything to do with ADTs. It's because Rust has both in-place variables and references. You aren't changing the variant of an existing ADT, you're replacing the entire ADT value with a new one. That replacement is visible in multiple places because the language allows you to take references to variables (the `&mut ex` expression). You can accomplish the same thing in C and C++ because they also have…

> You aren't changing the variant of an existing ADT, you're replacing the entire ADT value with a new one.

'Values' in Rust have no identity, except for their address. They're just a bunch of bytes in a row. What could it mean for a value to exist, except for it to be present at a set place in memory? If I have an instance of a Java class, and I change all the fields, I'd hardly say the instance has been replaced with a new instance.

If you insist, I'd say "Java's sealed classes are more limited in that when you have a bunch of references to the same thing, you can change the values in the fields of that thing (and have it be reflected in other references), but you can't change which variant it is." Call that thing a 'value' or a 'variable', it doesn't change the visible outcome compared to enums in Rust, or discriminated unions in C/C++.

Re: Algebraic Data Types for C99

#166
post #160

Earlier quoted context omitted.

> I have multiple times wondered which of those two approaches would be better in a given situation, often wanting some aspects of both. ADTs are closed to extension with new cases but open to extension with new functions, eg. anytime you want to add new cases, you have to update all functions that depend on the ADT, but you can add as many functions for that ADT as you like with no issues. Traits are open to extensi…

I suppose this is a genuine dichotomy, but I feel like it’s missing a more critical difference: ADTs cleanly represent data , even when nothing can be, or needs to be, extended from outside. For example, a result is a success value or an error. A stock order is a market order or a limit order, and nothing else , at least until someone updates the spec and recompiles the code. Situations like this happen all the time…

> For example, a result is a success value or an error. A stock order is a market order or a limit order, and nothing else, at least until someone updates the spec and recompiles the code.

But that's just it, specs are rarely complete because reality is fluid. For example, a result is a success or an error, until maybe you want an errors to prompt the user to correct something and then the computation can be resumed (see resumable exceptions).

Should you even have to recompile your code to handle new cases? Why can't you just add the new case, and define new handlers for the functions that depend on your ADT without recompiling that code? That's the expression problem.

Re: Algebraic Data Types for C99

#167
post #145

Earlier quoted context omitted.

> For one, the evidence seems as strong that people generally think backwards from the answer far more than they do forwards from the ingredients. Logic doesn't really have a direction, it works backwards or forwards. Even if you're solving a system "backwards", whatever that means, you still have to satisfy all of the necessary AND and OR constraints for a solution to be valid, so you're effectively still building A…

And this logic is how folks convince themselves that ball players are doing trigonometry when playing. It is just wrong. You can /model/ it that way. But you are making a symbolic model to justify how a solution is reached. Now, it can be frustrating to consider that this model could produce an agent that is better at the ball game than the players. But it is silly to think that means you have mirrored them.

> And this logic is how folks convince themselves that ball players are doing trigonometry when playing. It is just wrong.

You're attempting a sleight of hand here by saying "they're" not "doing trig". Clearly they are not doing anything like that consciously, but equally clearly some part of their brain is triangulating objects and predicting trajectories based on gradients, meaning that part is "doing trig and calculus" subconsciously. What else does it mean to "do something" if not "process X is isomorphic to process Y"?

> You can /model/ it that way. But you are making a symbolic model to justify how a solution is reached.

I really don't understand what you think people are doing when they're compiling a grocery list. They're clearly thinking, "I need x AND y OR I can substitute z".

Or if they're planning to paint their fence, they're thinking, "I need paint AND brushes AND I have to start before lunch OR I won't finish before dinner".

Re: Algebraic Data Types for C99

#168
post #82

Earlier quoted context omitted.

I can easily argue that people don't think in terms of boolean logic. For one, the evidence seems as strong that people generally think backwards from the answer far more than they do forwards from the ingredients. This is often why new things are so long to be discovered. It isn't that people couldn't have gotten there, but they didn't know to go for it. For two, addition is a wildly disparate thing everywhere we us…

> For one, the evidence seems as strong that people generally think backwards from the answer far more than they do forwards from the ingredients. Logic doesn't really have a direction, it works backwards or forwards. Even if you're solving a system "backwards", whatever that means, you still have to satisfy all of the necessary AND and OR constraints for a solution to be valid, so you're effectively still building A…

> Logic doesn't really have a direction, it works backwards or forwards.

Implication is one of the primitives in logic, and gives us several of the classic logical fallacies: affirming the consequent, denying the antecedent, fallacy of the converse, and fallacy of the inverse.

All of which are examples of trying to work logic as though it doesn't have a direction.

Re: Algebraic Data Types for C99

#169
If I ever implement a product from scratch again, discriminated unions with compiler enforced exhaustive pattern matching is a hard requirement. It’s too powerful to not have.

Re: Algebraic Data Types for C99

#170
Wikipedia has something interesting on this (how unions can be implemented using "class hierarchy in object-oriented programming"): https://en.wikipedia.org/wiki/Tagged_union#Class_hierarchies...

There is a lengthy blog post about the same stuff, except that the author doesn't seem to have come across the said wiki section yet: https://nandakumar.org/blog/2023/12/paradigms-in-disguise.ht...

Kudos to the dev of datatype99 for showing the problem with such ad-hoc methods in the readme right away.

Post reply on HN