Hacker News new | past | comments | ask | show | jobs | submit login

https://old.lispcast.com/what-are-product-and-sum-types/

That was easy to understand.

> You can have 100% type-safe, guaranteed at compile time code without null that can still represent the absence of data.

If it's a single score, I'd still want to use null / int. There no invalid states being represented, anything else is still unnecessary complexity.

> Nullness infects your data model and always comes out of nowhere in production and ruins your day.

A TS example: trying to do anything with 'score' where score is `null | number`, would be caught be the compiler.




>If it's a single score, I'd still want to use null / int.

In your example, you still have to manually check if there's a value every time, but this is not compiler-enforced. Should you forget, you will get a runtime crash at some point (likely in production at a critical time) with some kind of arithmetic error. This wouldn't be possible with a simple sum type.

Also, a sum type with units of Score(Int) and NoScore won't allow assignments of any other "null" instances. Null in one spot is interchangeable with null in any other spot, and this can lead to bugs and runtime crashes. Null should be avoided when possible.


The compiler would enforce Number-ness every time I try and run a function that takes a number, right?

Wouldn’t I still have to check for NoScore?

> a sum type with units of Score(Int) and NoScore won't allow assignments of any other "null" instances.

I get this part - I wouldn’t be able to assign ‘NewBornBaby’ (my name null) to ‘NoScore’ (my score null)


> The compiler would enforce Number-ness every time I try and run a function that takes a number, right?

> Wouldn’t I still have to check for NoScore?

No, because you differentiate between the sum type (e.g. Maybe in Haskell) and the number type at compile time. It's a small distinction - there will still be one or two places where you ask "is this a Maybe-score, or a Just-Score, or a No-Score", but the upside is that in all places you are very clear if a No-Score is possible, and you can't confuse the value with the error signal.

I.e. if you pass maybe-scores to something that computes the mean, you'll get a compiler error. The writer of the mean function doesn't need to care you've overloaded an error value onto your numbers.

The compiler support is the important part. Languages like C/C++ know sum-types just fine. They usually surface as sentinel values (NULL for ptrs, -1 for ints, etc) or unions with type fields. The stdlib offers it as std::optional<T>. As you progress along that spectrum, you get increasing compiler support there, as well.

One could even argue that sentinel values are a slightly better choice than Go's pairs, because they are closer to being sum-types than the strict product type that is go's (result, error) tuple - at least sentinels can't be simultaneously carrying a valid value and an error.


> I.e. if you pass maybe-scores to something that computes the mean, you'll get a compiler error. The writer of the mean function doesn't need to care you've overloaded an error value onto your numbers.

If I pass a null into something that calculates an average, taking numbers, the TS compiler will complain now. The writer of mean() (assuming mean() is typed) doesn’t have to know anything about my code.


This only works for non-sentinels. I.e. if "-1" happens to be the value indicating NoScore, I don't think the TS compiler can catch that?


Nobody is discussing using a magic number for null, nor is that an accepted practice in TS.


>Wouldn’t I still have to check for NoScore?

That's right, and the compiler will reject programs where you don't do this. It's a set of safety rails for your code. You pay a dev-/compile-time cost in exchange for your programs not exploding at runtime.


Yes but that’s no less work than checking for null in TypeScript. The parent said:

> In your example, you still have to manually check if there's a value every time, but this is not compiler-enforced.


Sure, it's the same amount of work, but you're forced to do it and the compiler will be able to reject invalid programs. In languages that allow null (including TS, it doesn't 100% enforce the stuff that Haskell, etc. does), you can skip that check, saving some work I suppose, at the risk of your code exploding at runtime. Having stack traces which jump across 50,000 lines of code because someone forgot to check for null somewhere sucks a lot.


TS wouldn’t let you skip that check though.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: