[HN Gopher] The Big Oops in type systems: This problem extends t...
___________________________________________________________________
The Big Oops in type systems: This problem extends to FP as well
Author : ksymph
Score : 49 points
Date : 2025-07-31 19:49 UTC (2 days ago)
(HTM) web link (danieltan.weblog.lol)
(TXT) w3m dump (danieltan.weblog.lol)
| dang wrote:
| Recent and related. Others?
|
| _The Big OOPs: Anatomy of a Thirty-Five Year Mistake_ -
| https://news.ycombinator.com/item?id=44612313 - July 2025 (181
| comments)
|
| _The Big Oops: Anatomy of a Thirty-Five-Year Mistake [video]_ -
| https://news.ycombinator.com/item?id=44596554 - July 2025 (91
| comments)
| hackyhacky wrote:
| Maybe I'm just missing something, but the "domain expert" that is
| described here is just... a function? The big win in Clojure is
| apparently using code instead of types? (defn
| apply-shipping-rules [order] (cond-> order (and
| (= :premium (:customer-type order)) (> (:order-
| total order) 100)) (assoc :shipping-cost 0)))
| sirwhinesalot wrote:
| Yes, the point of the article is that people should do this (as
| is common in Clojure) rather than try and encode the rules in
| the type system (be it as a class hierarchy or a sum type).
| GiorgioG wrote:
| So (I don't know Clojure) - is the author saying everything
| should be a map/dictionary? That sounds like complete chaos -
| I'm not an OOP proponent.
| rjknight wrote:
| The "domain expert" is the business-person who is, it is
| suggested, more capable of reading and comprehending the
| Clojure code than the Haskell code.
|
| Since there is an equivalence between types and propositions,
| the Clojure program also models a "type", in the sense that the
| (valid) inputs to the program are obviously constrained by what
| the program can (successfully) process. One ought, in
| principle, to be able to transform between the two, and
| generate (parts of) one from the other.
|
| We do a limited form of this when we do type inference. There
| are also (more limited) cases where we can generate code from
| type signatures.
|
| I think op's point is that the Clojure code, which lays the
| system out as a process with a series of decision points, is
| closer to the mental model of the domain expert than the
| Haskell code which models it as a set of types. This seems
| plausible to me, although it's obviously subjective (not all
| domain experts are alike!).
|
| The secondary point is that the Clojure system may be more
| malleable - if you want to add a new state, you just directly
| add some code to handle that state at the appropriate points in
| the process. The friction here is indeed lower. But this does
| give up some safety in cases where you have failed to grasp how
| the system works; a type system is more likely to complain if
| your change introduces an inconsistency. The cost of that
| safety is that you have two representations of how the system
| works: the types and the logic, and you can't experiment with
| different logic in a REPL-like environment until you have fully
| satisfied the type-checker. Obviously a smarter system might
| allow the type-checker to be overridden in such cases (on a
| per-REPL-session basis, rather than by further editing the
| code) but I'm not aware of any systems that actually do this.
| vips7L wrote:
| I honestly doubt a business person would be able to read
| Clojure. I've been programming for 15 years and it doesn't
| make any sense to me.
| senderista wrote:
| I've been reading and writing English for half a century
| and Chinese doesn't make any sense to me, so I doubt any
| ordinary human could read it.
| vips7L wrote:
| That's quite the false equivalence.
| michaelsbradley wrote:
| How so?
| hackyhacky wrote:
| > The secondary point is that the Clojure system may be more
| malleable - if you want to add a new state, you just directly
| add some code to handle that state at the appropriate points
| in the process.
|
| That's all certainly possible. But the same could be said of
| Python or JS. So if the big point here is "we can model
| business decisions as code!", I fail to see the innovation
| because we've been doing that for 50 years. Nothing unique to
| Clojure.
|
| You could even do it Haskell if you want: just store data as
| a Map of properties and values, emulating a JS object.
| sirwhinesalot wrote:
| I agree with the article, but I will note that we have a great
| tool for this problem that is (or at least can be) "statically
| typed": the relational model. Databases are precisely what you
| want for this sort of problem (even better if it's Datalog and
| you can encode rules that derive relevant information).
|
| Most mainstream languages are very poorly equipped to do
| relational modeling. ORMs are a disaster (object-relational
| mismatch) and you don't necessarily need an actual database
| running in the background.
|
| Clojure's approach is superior to the class hierarchy or sum type
| solution for this sort of very loose business domain modelling,
| for the reasons stated in the article, but it's also a local
| optima, and so is the "fat struct" solution (which is the
| statically typed equivalent). Even entity component systems are
| but a shadow of the relational model.
| jiehong wrote:
| It's part of the answer (the business logic stays as functions
| as expressed in the article).
|
| I'm glad people seem to have left behind the feeling that
| relational model is bad during the NoSQL era.
| mcphage wrote:
| > I will note that we have a great tool for this problem that
| is (or at least can be) "statically typed": the relational
| model. Databases are precisely what you want for this sort of
| problem
|
| Relational databases still lock you into a specific design, and
| trying to work contrary to how your application was designed
| 10-15 years ago leads to terrible performance, high costs, and
| bugs galore.
|
| It may be better than other options, but it's still not exactly
| a solved problem.
| rorylaitila wrote:
| I mostly agree. I have quipped once that I write "spaghetti and
| meatballs" code. The meatballs are the core domain objects,
| explicitly typed. The spaghetti is the business rules, untyped.
| With experience you get a good intuition where to draw the line.
| But the untyped code needs extensive testing.
|
| Where I disagree with the article is on refactoring. It's
| identically hard both ways. Migrating to new business rules while
| simultaneously running the old and new system is the hard part. I
| don't find static typing helps or hurts me in particular.
| Compiler warnings are useful, but my unit tests catch the dynamic
| parts as well. Either way a lot breaks and often needs temporary
| scaffolding between the versions.
| moomin wrote:
| I feel like this article is missing the point by a country mile.
| FP proponents very much know that requirements can change and
| wreak havoc with their type systems forcing them to change large
| numbers of likes. What the author is missing is that this is be
| welcomed and vastly preferable to the situation we find ourselves
| in with Python codebases where those lines still need updating
| but the code will happily run incorrectly if you fail to find
| them all. Switching off the alarm doesn't stop the fire
| spreading.
| qayxc wrote:
| I don't agree with that assessment. The problem is that,
| staying with your analogy, the fire alarm goes off every time
| someone has to use the toilet. Plus it's not just the alarm
| going off, it's the entire fire department showing up and doing
| a forced rearranging of the furniture throughout the entire
| building each time.
|
| And no, requirement changes don't have to cause that to happen
| and they don't have to wreak havoc throughout your application
| due to poor design decisions.
|
| It's fine to encode rules directly into the type system, but
| only for rules that are known to be fixed (or at least not
| likely to ever change) throughout the lifetime of the project.
| For many business rules, however, this unfortunately doesn't
| apply.
| fellowniusmonk wrote:
| A mereological nihilist will never have anything but primitive
| types.
|
| Everything more complex than those building block aren't in
| reality a Type.
|
| Reality doesn't consiste of: X type made up of these primitives
| and other defined sub-types and let's hide the primitives as far
| down as we can.
|
| It's instead primitives arranged X wise.
|
| Or mapped a little better to programming terminology: A Schema.
|
| It's about having the mental model that complex types can be
| useful as an abstraction but they aren't real and aren't worth
| fighting for or defending.
|
| Types are for devs, devs aren't for types.
| jayd16 wrote:
| It's supposed to break when your assumptions break. That's the
| whole point.
| jiggawatts wrote:
| I've watched a few good talks by people that have coded this kind
| of thing at the maximum level of scale and complexity, such as
| Amazon's checkout system.
|
| The endgame of this problem always turns into some sort of "log
| of events" with loosely coupled subscribers.
|
| A single state machine suffers from a combinatorial explosion of
| states as it has to handle every corner case, combinations of
| every scenario, etc...
|
| What if a single shopping basket contains _both_ a digital good
| and a physically shipped one? What if _some_ items are shipped
| separately and /or delayed? Etc...
|
| Instead the business rules are encoded into smaller state
| machines that listen to events on the log and pay attention only
| to relevant events. This avoids much of the complexity and allows
| the OOP types to remain relatively clean and stable over time.
|
| Now the "digital goods" shipping handler can simply listen to
| events where "delivery=authorized" and "type=digital", allowing
| it to ignore _how_ the payment was authorised (or just store
| credit!) and ignore anything with physical shipping constraints.
|
| It then writes an event that marks that line item in the shopping
| cart as "delivered", allowing partial cancellations later, etc...
| yehoshuapw wrote:
| while this does bring up a valid point,
|
| "classic" sql databases are still safer for many things then
| mongodb.
|
| it is _easier_ to do away with types and constraints, but in many
| cases they do end up being important safeguards
| dboreham wrote:
| The same abstract pattern exists with many (most?) annoying
| things in the computing field: people focus on solving problem
| #1 but it turns out there's a complementary problem #2 that is
| now worse, that they either didn't know about or didn't much
| care about.
___________________________________________________________________
(page generated 2025-08-02 23:00 UTC)