Live data from Hacker News

Algebraic Data Types for C99

github.com

131–140 of 233 posts

Re: Algebraic Data Types for C99

#131
post #130

Earlier quoted context omitted.

This is where I like to cite Dijstra's "Go To Statement Considered Harmful". What a lot of people miss about that paper is that he wasn't just talking about goto statements. He was also making a more general observation about how more powerful and general programming language features are not necessarily desirable, because they tend to adversely impact developer productivity. The reason I, as a user, prefer structure…

That's exactly where I am. Pattern matched sum types "feel great" to code in to an expert because they are a concise and reasonably tight way to express the otherwise boring "enumerate over possibilities" code. But they're hard to read for anyone who isn't an expert on not just the language but the type in question (c.f. Rust's Option() idioms all looks like line noise to newbies, etc...). And that's a bad trade. In…

You've got to distinguish syntax from semantics, though. I agree, it's easy to turn a big semantic win into a net readability loss if you choose to represent it with an overly terse syntax that promotes code golf.

Re: Algebraic Data Types for C99

#132

One of the crimes of modern imperative programming languages is not having ADTs (except maybe Rust) built-in. It is such a basic mental model of how humans think and solve problems. But instead we got inheritance and enums which are practically very primitive.

May not be built in but many mainstream languages such as typescript have libraries or the tools to easily implement them.

Re: Algebraic Data Types for C99

#133
post #121

Earlier quoted context omitted.

This often comes up when writing a function which returns a wrapper over a generic type (like Option ). If your Option type is T | null, then there's no way to distinguish between a null returned by the function or a null that is part of T. As a concrete example, consider a map with a method get(key: K) -> Option . How do you tell the difference between a missing key and a key which contains `null` as a value?

This is trivial to model by making your type `T | null | Missing`.

Maybe trivial to “work around” but there is a difference, ay?

With this type you would have to check/match an extra case!

The type you use there also takes more memory than Option or Maybe. So it has some other downsides.

Re: Algebraic Data Types for C99

#134
post #121

Earlier quoted context omitted.

This often comes up when writing a function which returns a wrapper over a generic type (like Option ). If your Option type is T | null, then there's no way to distinguish between a null returned by the function or a null that is part of T. As a concrete example, consider a map with a method get(key: K) -> Option . How do you tell the difference between a missing key and a key which contains `null` as a value?

This is trivial to model by making your type `T | null | Missing`.

Or just using Option since you would have Some or None in that case.

Re: Algebraic Data Types for C99

#135
post #78
post #49

Earlier quoted context omitted.

I'm a huge proponent of ADTs being a more comprehensible way to write code than some of the alternatives. But I do have to agree with you that there isn't really evidence that this is a basic mental model. However What we do see is a bunch of mathematical disciplines that end up creating properties like: AND, OR, Universal, Existential, Implication, (and a few others). They end up in places like: set theory, type the…

Appearing in symbolic reasoning tools we have invented doesn't really support them being how brains work, though? This is akin to saying that gears are how nature works because gears are everywhere in how we build things. I could maybe buy that with "friction" being a fundamental thing, but feels like a stretch for the other. Now, I should add that I did not mean my question to be a criticism of them! I'm genuinely c…

I think you're right to point out that it's too strong a claim to say that sum types are a basic building block of thought, although I believe they are very useful in coding regardless of that claim.

There is the still the ongoing debate about how much human perception and human reason are shaped by cultural forces vs. universal forces (where the latter asserts humans reason in the same/similar ways).

There's evidence that certain optical illusions don't work across cultures for example (I seem to remember those in Western countries have a tendency to mentally group things in rectangular boxes). The exact balance between cultural and universal forces isn't known and I doubt we could say anything about sum types in that regard.

Re: Algebraic Data Types for C99

#136
post #97

Earlier quoted context omitted.

Isn't that a completely useless distinction? For all purposes and intents, the "b" type in L and R should be treated the same, no? What do you gain by not doing that??

This often comes up when writing a function which returns a wrapper over a generic type (like Option ). If your Option type is T | null, then there's no way to distinguish between a null returned by the function or a null that is part of T. As a concrete example, consider a map with a method get(key: K) -> Option . How do you tell the difference between a missing key and a key which contains `null` as a value?

`T | null` is equivalent to T. You can assign null to `T`>

It's like saying `string | "foo"` it is simply `string` due to subtyping.

Re: Algebraic Data Types for C99

#137
post #97
post #81

Earlier quoted context omitted.

Sum types are disjoint unions. This `T` has three cases L = { tag: "a", payload: string } | { tag: "b", payload: number } R = { tag: "b", payload: number } | { tag: "c", payload: boolean } T = L | R whereas a proper sum type `L + R` would have four.

Isn't that a completely useless distinction? For all purposes and intents, the "b" type in L and R should be treated the same, no? What do you gain by not doing that??

No, it isn't "completely useless".

If you have a function that will normally return a string, but can sometimes fail due to reasons, you may wish to yield an error message in the latter case. So you're going to be returning a string, or a string.

It's not what the content of the data is; it's how you're supposed to interpret it. You have two cases, success and failure, and control will flow differently depending on that, not based strictly on the type of data at hand. We just model those cases in a type.

Re: Algebraic Data Types for C99

#138
post #81
post #48

Earlier quoted context omitted.

TS narrows union types cases based on conditionals like "if" (called discriminated unions in the docs in the past), and supports exhaustiveness checks. How do they differ in functionality from sum types?

Sum types are disjoint unions. This `T` has three cases L = { tag: "a", payload: string } | { tag: "b", payload: number } R = { tag: "b", payload: number } | { tag: "c", payload: boolean } T = L | R whereas a proper sum type `L + R` would have four.

As you've demonstrated, you can always construct sum types in typescript with the use of explicit discriminants:

    T = {tag: "L", payload: L} | {tag: "R", payload: R}
The real issue is typescript doesn't have pattern-matching, which make operating on these sum types inelegant

Re: Algebraic Data Types for C99

#140
post #68

Let's say you have a C program to write, and you really want exhaustive pattern matching on the tags of unions (which is what Datatype99 provides: "Put simply, Datatype99 is just a syntax sugar over tagged unions"). Let's say further that you already know Rust exists, and aren't going to use it for reasons that anyone writing a C program already knows. At least consider Zig. Here's a little something I wrote in Zig t…

Seems like Nim can be useful too, plus it compiles to C. https://gist.github.com/unclechu/eb37cc81e80afbbb5e74990b62e...

I've been a Nim respecter for many years, it's slept on in general as a language.

The difference here is that Nim compiles to C and you can turn the garbage collector off: Zig compiles C and there's no garbage collector. That means the entire standard library is available when generating object code. It's also trivial to opt-in to the C ABI on a fine-grained basis, by defining a function or struct with the extern keyword.

I believe this is still fairly current about the difficulties of building Nim dylibs for C programs: https://peterme.net/dynamic-libraries-in-nim.html

I expect Nim will stabilize about where D has: it will have a dialect of the language which, with relatively painless accommodations, is able to produce object code which speaks C ABI. Zig is different. The language is relentlessly focused on providing a better alternative to C while occupying the same niche, and a lot of design time has been spent on making it practical to take an existing C program and start writing the new parts of it in Zig.

It's a good language, Nim, and getting better. I'd recommend it for someone who is considering Go, for example.

Post reply on HN