Live data from Hacker News

The compiler is your best friend

blog.daniel-beskin.com

101–110 of 149 posts

Re: The compiler is your best friend

#101
post #40

Earlier quoted context omitted.

I think you’re conflating 'conciseness' with 'correctness.' The 'clutter' you're describing in Swift like, having to explicitly define an Enum instead of using a vertical bar |, is exactly what makes it more robust than TS for large-scale systems. In TypeScript a union like string | number is structural and convenient, but it lacks semantic meaning. In Swift, by defining an Enum, you give those states a name and a pu…

This is just cope. Swift's compiler doesn't choke because your logic is too ambiguous, it chokes because it's simply too slow at inferring types in certain cases, including a very common case where you write a normal Swift UI view. There is nothing ambiguous about that. Secondly, I'm not sure why you think the mountains of boilerplate to replace | leads to semantic meaning. The type identifier itself is sufficient.

| is a massive footgun because it's usually exclusive in practice and occasionally silently inclusive. Having it in a language is a mistake, even if it superficially seems to make things easier, just like e.g. null.

Re: The compiler is your best friend

#102

I'm not a fan of the recent trend in software development, started by the OOP craze but in the modern day largely driven by Rust advocates, of noun-based programming, where type hierarchies are the primary interface between the programmer and the code, rather than the data or the instructions. It's just so... dogmatic. Inexpressive. It ultimately feels to me like a barrier between intention and reality, another abstr…

"Bad programmers worry about the code. Good programmers worry about data structures and their relationships."

> It's just so... dogmatic. Inexpressive. It ultimately feels to me like a barrier between intention and reality, another abstraction.

On the contrary, it's a much more effective way to express intention when you have a language that can implement it. Programmers in C-family languages waste most of their time working around the absence of sum types, they just don't realise that that's what they're doing. Yes it is an abstraction, all programming is abstraction.

Re: The compiler is your best friend

#103

> A common pattern would be to separate pure business logic from data fetching/writing. So instead of intertwining database calls with computation, you split into three separate phases: fetch, compute, store (a tiny ETL). First fetch all the data you need from a database, then you pass it to a (pure) function that produces some output, then pass the output of the pure function to a store procedure. Does anyone have a…

> Does anyone have any good resources on how to get better at doing "functional core imperative shell" style design?

Hexagonal architecture[0] is a good place to start. The domain model core can be defined with functional concepts while also defining abstract contracts ( abstractly "ports", concretely interface/trait types) implemented in "adapters" (usually technology specific, such as HTTP and/or SMTP in your example).

0 - https://en.wikipedia.org/wiki/Hexagonal_architecture_(softwa...

Re: The compiler is your best friend

#104
post #8
post #4

Earlier quoted context omitted.

A comment "this CANNOT happen" has no value on itself. Unless you've formally verified the code (including its dependencies) and have the proof linked, such comments may as well be wishes and prayers. Yes, sometimes, the compiler or the hardware have bugs that violate the premises you're operating on, but that's rare. But most non pure algorithms (side effects and external systems) have documented failure cases.

> A comment "this CANNOT happen" has no value on itself. I think it does have some value: it makes clear an assumption the programmer made. I always appreciate it when I encounter comments that clarify assumptions made.

>> A comment "this CANNOT happen" has no value on itself.

> I think it does have some value: it makes clear an assumption the programmer made.

To me, a comment such as the above is about the only acceptable time to either throw an exception (in languages which support that construct) or otherwise terminate execution (such as exiting the process). If further understanding of the problem domain identifies what was thought impossible to be rare or unlikely instead, then introducing use of a disjoint union type capable of producing either an error or the expected result is in order.

Most of the time, "this CANNOT happen" falls into the category of "it happens, but rarely" and is best addressed with types and verified by the compiler.

Re: The compiler is your best friend

#105
post #13
post #11

Earlier quoted context omitted.

But if you spell that `assert(false)` instead of as a comment, the intent is equally clear, but the behavior when you're wrong is well-defined.

Better yet, `assert(false, message)`, with the message what you would have written in the comment.

Swap the parameters around for C++ and similar langs where `assert(a, b)` evaluates the same as `(void) a; assert(b)`.

Re: The compiler is your best friend

#106
post #97
post #93

Earlier quoted context omitted.

There is no recovery. When an invariant is violated, the system is in a corrupted state. Usually the only sensible thing to do is crash. If there's a known bug in a program, you can try and write recovery code to work around it. But its almost always better to just fix the bug. Small, simple, correct programs are better than large, complex, buggy programs.

> Usually the only sensible thing to do is crash. Correct. But how are you testing that you successfully crash in this case, instead of corrupting on-disk data stores or propagating bad data? That needs a test.

> Correct. But how are you testing that you successfully crash

In a language like rust, failed assertions panic. And panics generally aren't "caught".

> instead of corrupting on-disk data stores

If your code interacts with the filesystem or the network, you never know when a network cable will be cut or power will go out anyway. You're always going to need testing for inconvenient crashes.

IMO, the best way to do this is by stubbing out the filesystem and then using randomised testing to verify that no matter what the program does, it can still successfully open any written (or partially written) data. Its not easy to write tests like that, but if you actually want a reliable system they're worth their weight in gold.

Re: The compiler is your best friend

#107
post #97

Earlier quoted context omitted.

> Usually the only sensible thing to do is crash. Correct. But how are you testing that you successfully crash in this case, instead of corrupting on-disk data stores or propagating bad data? That needs a test.

> Correct. But how are you testing that you successfully crash In a language like rust, failed assertions panic. And panics generally aren't "caught". > instead of corrupting on-disk data stores If your code interacts with the filesystem or the network, you never know when a network cable will be cut or power will go out anyway. You're always going to need testing for inconvenient crashes. IMO, the best way to do thi…

> In a language like rust, failed assertions panic. And panics generally aren't "caught".

This thread was discussing debug_assert, where the assertions are compiled out in release code.

Re: The compiler is your best friend

#108

> A common pattern would be to separate pure business logic from data fetching/writing. So instead of intertwining database calls with computation, you split into three separate phases: fetch, compute, store (a tiny ETL). First fetch all the data you need from a database, then you pass it to a (pure) function that produces some output, then pass the output of the pure function to a store procedure. Does anyone have a…

What I'm currently doing could be called compute-fetch-store: the compute part is done entirely in the database with SQL views stacked one on top of the other. Then the program just fetches the result of the last view and stores it where it needs to be stored.

Stacked views are sometimes considered an anti-pattern, but I really like them because they're purely functional, have no side-effects whatsoever and cannot break (they either work or they don't, but they can't start breaking in the future). And they're also stateless: they present a holistic view of the data that avoids iterations and changes how you think about it. (Data is never really 'transformed', it's simply 'viewed' from a different perspective.)

Not saying that's the only way, or the best way, or even a good way! But it works for me.

I think it would apply well to the example: you could have a view, or a series of views, that compute balance top-ups based on a series of criteria; then the program would read that view and send email without doing any new calculation.

Re: The compiler is your best friend

#109

> A common pattern would be to separate pure business logic from data fetching/writing. So instead of intertwining database calls with computation, you split into three separate phases: fetch, compute, store (a tiny ETL). First fetch all the data you need from a database, then you pass it to a (pure) function that produces some output, then pass the output of the pure function to a store procedure. Does anyone have a…

Google Testing blog: https://testing.googleblog.com/2025/10/simplify-your-code-fu...

Re: The compiler is your best friend

#110
post #86

Earlier quoted context omitted.

Ideally, if you can convince yourself something cannot happen, you can also convince the compiler, and get rid of the branch entirely by expressing the predicate as part of the type (or a function on the type, etc.) Language support for that varies. Rust is great, but not perfect. Typescript is surprisingly good in many cases. Enums and algebraic type systems are your friend. It'll never be 100% but it sure helps fil…

Until you have a bit flip or a silicon error. Or someone changed the floating point rounding mode.

> Until you have a bit flip

These are vanishingly unlikely if you mostly target consumer/server hardware. People who code for environments like satellites, or nuclear facilities, have to worry about it, sure, but it's not a realistic issue for the rest of us

Post reply on HN