Remix.run Logo
dllthomas 2 days ago

> How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system?

I think you're confusing "make illegal states unrepresentable" with "parse, don't verify"? If your type cannot represent any invalid states, there's no way you can reach them through mutation.

matt_kantor 2 days ago | parent [-]

The "sequences of mutations" phrasing made me think they were talking about stuff like state machines or handles for external resources—for example calling `databaseConnection.close()` on an already-closed connection, which is usually a runtime error (or maybe a no-op).

bunderbunder a day ago | parent [-]

I don't see the problem with state machines. When you're dealing with something that handles external input, "the input is invalid" is, for your program, a valid state. For example a regular expression engine doesn't try to make it impossible to pass in a string that doesn't match; it just returns a result indicating that the string didn't match.

Database connections are exactly what the "functional core, imperative shell" principle is about. The idea is to handle all I/O at the boundary. So shell is never passing open database connections into the functional core; it's instead retrieving everything that's needed up front so that the core can be deterministic.

"Functional core, imperative shell" might actually be my favorite of the principles, because it makes code so much easier to test. Every time I come across a codebase whose test suite has to make intense use of mocking to cope with how they allowed concurrency to spread throughout every single layer and module in the application, I get a little bit sad that nobody did its authors the service of teaching them that you don't actually need to make your own life hard like that.

It also tends to result in less code to understand and maintain overall, IME. Because if you limit the number of places where an error is even possible, you don't get stuck having to litter your codebase with excess (and often repetitive) error handling code.

matt_kantor a day ago | parent [-]

I agree with all of that, but I don't see what it has to do with this thread of the discussion.

The comment I replied to was wondering what exactly greener_grass was referring to when they said:

> outside of Functional Programming […] How can you "Make illegal states unrepresentable" with mutable state and sequences of mutations that cannot be enforced with the type system?

And my guess was that they had illegal transitions between states in mind. Those are hard/impossible to statically reason about when the program is written as "sequences of mutations" (particularly when aliasing is possible).