Hacker News new | ask | show | jobs
by yoloyoloyoloa 1573 days ago
Functional programming (and how it is even useful)
3 comments

A way I heard it explained that made it make sense to me is this:

FP is basically making all of your inputs and outputs explicit. In OOP if you have some bool on a class that affects what a method does, that private bool is technically an extra parameter for that method, even if it doesn't get passed in. Similarly if a method changes some integer on its parent class, in a sense that integer is technically part of what gets returned by the method.

Functional programming forces you to actively think about all of the extra inputs and outputs of your code. In turn, this helps you avoid unintended side effects from the internal workings of a method etc. and the bugs that they can cause.

While the ideas behind FP are useful and can help you think about your coding in a cleaner way, I also think they can be overdone and taken to almost religious extremes. And it can come with a cost of worse performance, more difficult to write/maintain code, etc. , though done right it can have the opposite effect. Done right, it can also make certain aspects of asynchronous and parallel programming easier to work with.

FP, OOP, and all of the other programming paradigms have useful concepts, but purism for any paradigm can lead to all kinds of problems. The obsession with "state" that you see from some FP evangelists is especially annoying, because it ignores that in almost any useful program there will be an irreducible amount of state that has to be tracked and dealt with.

It's useful because it's simple.

You are essentially working with functions that receive input and return output, and are not allowed to change anything outside them.

The simplicity is in that you create multiple levels of abstractions by only combining functions: low-level functions do some "dirty" work that the program needs to perform (slice cucumber, open oil, pour oil, add salt, etc); you then combine a couple of such low-level functions in another set of functions - making it a bit more abstract (make salad, prepare table); then you combine these abstract functions into even more abstract ones(prepare dinner), etc. There will be also some "helper" abstract functions that can be reused in different contexts (slice something, open something, put some food on the table, etc).

Because each function - no matter the level of abstraction - only takes input and produces output - it's very simple to write, read, reason about, and test.

Obvious question you might have right now: how can you write a useful program if you can't change anything outside the function or interact with the outside world? Well, that's the "dirty" part of writing a FP program. You just write your program trying to keep it as "pure" as possible, pushing the "dirty" parts away as much as possible. Think Layered Architecture but for programs. You keep the "outer layers" of your program "dirty" - reading/writing to the DB, making network calls, etc - and preserve the "purity" of as many "inner layers" as possible. For example, when you want to write to a DB in one of "inner layers" which should stay "pure" - you can return your "desire to write to a DB" as a string(or however you encode it) to your outer layers, which will then perform the actual operation by actually talking to the DB. The inner function stays pure because it doesn't actually talk to the DB, it only returns instructions for outer layers to talk to the DB.

Depending on the language you are using it might be either beautiful and easy, or extremely awkward and overly complicated. Most of the time it's something in between.

Hope this helps.

A lot of things can be conceptualized as an operation that takes some stuff, and gives back some new stuff, and does absolutely nothing else.

It's like a lot of other strong constraints on complex endeavors. If you run with it you can get surprisingly far, but only by twisting your understanding of how to do things. Whether that's good or not seems highly personal though.

This is a great explanation. As someone who's a relatively recent convert to FP and am currently writing both imperative and functional code I will test my own depth of knowledge by trying to explain it

1. In traditional (imperative) languages you can have functions that deliberately or accidentally either modify the data they were given or do some other stuff that changes data somewhere else in your program or the computer. Let's take an example of each. Say you have a function that adds two numbers a and b. In imperative languages you could return a+b into b. In other words the sum(a,b) operation would change b. That's not cool in FP: you are (almost never) allowed to change the input you received. This way whenever the function is called with a and b it always returns the sum (otherwise if you call sum(a,b) again you will pass in a modified b).

This makes every function guaranteed to always return the same value for the same arguments. If you accidentally try to change a parameter in your code it will fail.

But you still need to make changes to the world - you do so by chaining these functions and using the output from one as input to the next and so on.

Last but not least, functions that have side effects (e.g. print to the screen) are marked differently and usually their effect is isolated.

Haha thanks love this explanation!