Hacker News new | ask | show | jobs
by Barrin92 1544 days ago
Personally I never really understood the benefit of thinking in functional terms about things that are conceptually more naturally thought of as persistent objects.

For example when the author picks "parents" or "children" or in general maybe any entity of any kind, I don't understand what functional thinking gets me. Sure instead of manipulating mutable objects I can think of every entity being 'one thing' at 'one moment in time' and I do a gazillion state transitions that all return new things and I have never mutated anything, but not only is that bad from a performance standpoint, I never understood how that's clearer or easier, it just seems like a tortured metaphor.

Monads and other functional tools are being brought up in this thread as well but I always had the same problem. Sure, I can stuff my operations into a box, say it's the "state box" and then everything is neat but for what purpose? I still remember when I was in university I rewrote a tetris clone I had made into Haskell. It took a lot of time and weird error messages, but at the end of the day translating all my imperative or object like procedures into functional paradigms didn't make the program any easier to understand.

The places where I think functional programming is natural is for something like accounting, data processing or version control systems. Where I really do want to think of every state as its own, immutable little world, where keeping track of changes pays off, and so on. In a lot of domains it to me seems ill-suited.

6 comments

Because it reduces the number of things you have to keep in your head and the uncertainty about what you don't know.

Using monads and a "state box", it's not enough to make a change to the context of the box, you also have to pass the box onto the next entity/process that wants to change it, or nothing will effectively change (other than burning cpu cycles).

This also means that it suddenly becomes easy to find out what things happened to the box content before, because all you have to do is check what the one who was passing the box to you was doing with it.

If your program is strictly linear than using state boxes is merely syntactic overhead. But as soon as that's not the case anymore, it suddenly becomes important.

I remember very well the time when I had to deal with bugs where I was operating on data that looked different than I expected and I had a hard time to figure out how it came to that.

But even worse are bugs from concurrency and also the "magic" and workarounds for dealing with it when "state boxes" are not available or known to the developer. Now you look at a line of code while knowing that stuff is running somewhere else, impacting what you are working on in a potentially uncontrolled manner.

I find that very unproductive.

I’ve encountered three benefits to functional programming:

- immutability and function application perspective “play nice” with distributed applications, both for distributing work and idempotence

- in a similar vein, the “everything persists, apply functions” perspective works nicely in data science for finance, where you need auditable calculations

- and similarly, but more broadly, if you’re implementing a workflow engine, then the functional perspective is a clear winner: you’re literally writing a program to manipulate and apply functions!

Friends have told me a fourth:

- composition and lenses are nice for UI

You are absolutely right that there are domains that are just inherently more stateful and not easily leant to a functional style. What I've found so far on my functional journey is that it can be handy to lean into this aspect of "punishing" certain problems. It kind of forces you to think more explicitly about mutation and when you actually want to invoke it. Thus you end up writing a lot in terms of transactions and hypotheticals - "if I get a POST /users request, what SQL queries do I emit?"

You have execution engines (a web framework and sql engine) that encap a block of pure function. There's no side effects (save maybe logging) whatsover inside your mini-program.

FP patterns like monads let you compose blocks of execution so you can have bigger mini-programs that are more ergonomic to write.

SQL query builders are (I think) a kind of Monad. You can compose selections, filters, groups, etc without ever executing, building up a big "what if" into a transaction. No side effects until you execute, it's pure data in data out. Which means for example you can write true unit tests without mutating a "real" db by composing transactions together.

FP falls apart when you have to reason about "nuts and bolts" of actual computation models, IO, network, time, RNGs, DMA, gpus, large data arrays. FP acolytes say "just let the computer/compiler do it" eg write in FP and compile to optimized imperative machine code, but someone still has to write drivers at the end of the day.

immutability is amazing. you can make assumptions about the data that you have received, it's friendly to caching, it's friendly to crashes, it's very easy to reason about.

it's even more amazing when you enter a territory of multithreading/multi processor.

But the real world is mutable. The authors example of a GUI program is a good one: the user wants to change the state of the program, and expects the new state to be reflected in the GUI. You can model this in fp, but it feels like I'm standing on my head. The user wants to modify a glyph, and the program should modify that glyph. It's the simplest way of modeling what is happening.
At some point you need to break the paradigm to do anything useful, be it some IO, or whatever. Working in a FP way for as long as appropriate will make large parts of your program better testable and avoid large amounts of unwelcome surprises. If at some point you got some result, which you want to let affect the real world, then you can still limit mutation to that place, where you got the result. I would avoid that for as long as possible, to have larger parts of my program be easy to reason about.

GUI has been one of the strongholds of mutation-encouraging paradigms though. It should be said. I would use it as an example, where one probably might need mutation for performance reason and for the reason, that I find it a bit too overheady to create a new widget for every small change. I don't know enough about how some languages try to solve this and stay declarative or functional, to comment on that.

this is me being cheeky, but how do you know that the real world is mutable?

if you believe in super-determinism and that everything progresses based on predetermined laws you can take the view that space+time already exists in all its past and future and that nothing happens.

or, quantum mechanics in the multiple world interpretations. you can say that the state of the world literally splits whenever there is an event.

i guess the metapoint here is that just because something seems mutable does not mean it’s really mutable and that mutable and immutable only make sense at certain levels of matter organization (are atoms mutable? what about photons? what about quarks?)

Late to the party here, but what you propose is not being cheeky.

Rich Hickey (Clojure creator) himself has a similar, interesting, and compelling take on time and mutability [0]. This perspective is the underlying basis of Datomic.

[0] - https://youtu.be/ScEPu1cs4l0

i found that people are more open to this idea if they believe i am kidding.
Rich Hickey made a great talk on this in 2009 https://youtu.be/ScEPu1cs4l0

An other way to think about it is that the programs we write do not literally simulate entities (users, other domain objects like books, students, professors, classes, etc). Our software simulates the record keeping devices that store /protect information regarding these entities. We are most of the time writing information processing systems, not simulation systems and information accumulates, rather than updates in places. Like, e.g. when there's a new president elected, we don't all go out with our erasers and remove all references to change sentences from "The President is Donald Trump" to say "The President is Joe Biden", instead we accumulate new facts without remembering the old.

*without forgetting the old.
> Personally I never really understood the benefit of thinking in functional terms about things that are conceptually more naturally thought of as persistent objects.

Like the vast majority of software developers world wide, who just want to get things done and/or earn money.

The simple truth is: A minority prefers purely functional programming. The vast majority prefers procedural programming with functional elements.

For some reason, those who prefer pure functional programming often try to convince everyone else their way is the best way in an obnoxious way.