1

I likely am asking a very stupid question here, please forgive me.

I am a Java and C# backend-engineer with relatively good knowledge of OOP design patterns. I have recently discovered the debate about OOP vs functional programming and the question i simply can't wrap my head around is this: If there is no state, then how could we update an element based on user input?

Here's a small example showing the problem i am facing (i am aware that JS is not a strictly functional language, but i think it shows my problem relatively well.):

Let's say hat we have a small web page that simply displays a counter and increments its value every time the user clicks a button:

<body>
    The count is <span id="data">0</span><br>
    <button onClick="inc()">Increment</button>
</body>

Now there's the strictly imperative approach, using a counter variable to store the counter's state:

let data;
window.onload = function(){
    data = document.getElementById("data");
}
let counter = 0;
function inc(){
    data.innerHTML = ++counter;
}

A more functional approach (at least in my understanding) would be the following code:

let data;
window.onload = function(){
    data = document.getElementById("data");
}
function writeValue(val){
    data.innerHTML = val;
}
function doIncrement(val){
    return val + 1;
}
function readValue(){
    return parseInt(data.innerHTML);
}
function inc(){
    writeValue(doIncrement(readValue()));
}

The issue i am facing now is that, while the data variable is never altered, data's state still changes over time (every time the counter is updated). I do not really see any real solution to this. Of course, the counter's state needs to be tracked somewhere in order for it to be incremented. We could also call document.getElementById("data") every time we need to read or write the data, but the problem essentially remains the same. I have to somehow track the page's state in order to process it later.

Edit: Note that i have reassigned (which, i am told, is a bad thing in FP) the value val to the variable innerHTML in the writeValue(val) function. This is the exact line at which i am starting to question my approach.

TL;DR: how would you handle data that is naturally subject to a change in a functional way?

ndrscodes
  • 41
  • 6
  • 3
    "*If there is no state, then how could we update an element based on user input?*" FP is all about managing state and side-effects. It's not about having no state at all. Unless you want rather useless programs. – VLAZ Mar 29 '22 at 20:53
  • @VLAZ thank you for the input. Would my second code example be categorized as an example of functional programming then, or am i still missing something here? – ndrscodes Mar 29 '22 at 20:55
  • 3
    `writeValue` and `readValue` are not pure functions. That's not FP, rather "programming with functions". `doIncrement` is ok, it's the actual business logic extracted in a pure function (from old state to new state), and the rest could be considered "glue code". But storing the state in the DOM instead of in `let counter` is neither FP nor a good practice. – Bergi Mar 29 '22 at 23:41
  • @Bergi could you provide an example of how this example could be "FP-ified"? – ndrscodes Mar 30 '22 at 10:01
  • 1
    @ndrscodes Not really. The DOM must be changed (mutated), and event handlers need to have effects. There must be some state in your application. See also [this](https://stackoverflow.com/a/50667879/1048572). – Bergi Mar 30 '22 at 10:09
  • 2
    @ndrscodes For understanding the FP paradigm better, I'd recommend you take a look of [this example of implementing a counter](https://elm-lang.org/examples/buttons) in the [elm language](https://elm-lang.org), which compiles to JS with a small runtime as the "imperative shell" as Mark called it – Bergi Mar 30 '22 at 10:13
  • @Bergi thank you, this actually helped me a lot! Did i understand this correctly then: The purest FP way of doing something like that would be having the browser display a model, and instead of updating the model we replace it with an entirely new model with an altered state compared to the first one? So the displaying application is responsible for simply rendering the new model instead of actually changing a part of what is being displayed? – ndrscodes Mar 30 '22 at 15:56
  • 1
    @ndrscodes Yes. At least that's the code we're programming. The compiler will then of course choose to optimise this, and the rendering engine can easily find what things changed in the new model so that it needs to re-render only that part. – Bergi Mar 30 '22 at 16:22

1 Answers1

2

This question seems to originate from the misunderstanding that there's no state in Functional Programming (FP). While this notion is understandable, it's not true.

In short, FP is an approach to programming that makes an explicit distinction between pure functions and everything else (often called impure actions). Simon Peyton-Jones (SPJ, one of the core Haskell developers) once gave a lecture where he said something to the effect that if you couldn't have any side effects, the only thing you could do with a pure function would be to heat the CPU, whereafter one student remarked that that would also be a side effect. (It's difficult to find the exact source of this story. I recall having seen an interview with SPJ where he related the story, but searching the web for a quote in a video is still hard in 2022.)

  • Changing a pixel on the screen is a side effect.
  • Sending an email is a side effect.
  • Deleting a file is a side effect.
  • Creating a row in a database is a side effect.
  • Changing an internal variable that, via cascading consequences, causes anything like the above to happen, is a side effect.

It is impossible to write (useful) software that has no side effects.

Furthermore, pure functions also don't allow non-deterministic behaviour. That excludes even more necessary actions:

  • Getting a (truly) random number is non-deterministic.
  • Getting the time or date is non-deterministic.
  • Reading a file is non-deterministic.
  • Querying a database is non-deterministic.
  • Etc.

FP acknowledges that all such impure actions need to take place. The difference in philosophy is the emphasis on pure functions. A pure function has many desirable traits (predictability, referential transparency, possible memoization, testability) that makes it worthwhile to pursue a programming philosophy that favours such functions.

A functional architecture is one that minimises the impure actions to their essentials. One label for that is Functional Core, Imperative Shell, where you push all impure actions to the edge of your system. That would, for example, include an HTML counter. Actually changing an HTML element stays imperative, while the calculation required to produce the new value can be implemented as a pure function.

Different languages have varying approaches to how explicitly they model the distinction between pure functions and impure actions. Most languages (even 'functional' languages like F# and Clojure) don't explicitly make that distinction, so it's up to the programmer to keep that separation in mind.

Haskell is one language that famously does make that distinction. It uses the IO monad to explicitly model impure actions. I've attempted to explain IO for object-oriented programmers using C# as an example language in an article: The IO Container.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • So could both of my examples serve as examples of FP then? After all, the amount of variables with varying states during execution is still kept to a minimum. – ndrscodes Mar 30 '22 at 10:00
  • 1
    @ndrscodes I'm no JavaScript programmer, but as far as I can tell, the only pure function suggested is `doIncrement`.... That doesn't strike me as a particular good ratio of pure functions to impure actions... – Mark Seemann Mar 30 '22 at 20:10