I would be interested in your views about OOP; it shares some ideals with FP, namely, encapsulation. OOP puts a lot of emphasis on encapsulating state, and binding the associated behavior to it so that no other part of the program can access it and cause problems, and so that contracts can be expressed by clean, abstract interfaces. It also typically uses procedural workflow and algorithmic and adds to it classes as a code organisation paradigm. OOP is boxes; stuff is in boxes so you don't see it.

FP tries to do that but with behavior, so that any given operation can be cleanly plugged into other behavior via function composition and higher-levels such as maps, folds and their math-ey equivalents functors and monads. FP is pipes. Stuff goes in, other stuff comes out, and we have an elaborate system of pipe ends to make sur one's out fits into the other's in.

OOP is good when a lot of persistent state has to hang around; objects in a game, objects floating around in a GUI window, client, transaction, product objects in a commercial application that must be created, viewed, modified, deleted. Boxes in a warehouse, bills and client data in boxes, running and shooting boxes in your Xbox.

FP is good when data has to come in, be transformed, and get out. Text treatment for example. grep is obviously made out of super-low-level procedural C, but it would be a perfect fit for the FP model: a bunch of text comes in, gets chopped into lines, a search algo is performed on each one, and it all gets spitted out, possibly into another, well, pipe. Compilers are also a nice fit: yet some more text comes in, it's chopped into a flat list of tokens, it's parsed into an abstract syntax tree, then into a type-checker, then a few other pieces of optimisation piping, and finally gets spit out as some other lower-level representation.

Server-side scripting is the most common real-life application of functional-ish langs; a request comes in, a few DB requests are made, the return values are chopped into HTML (or whatever mid-level representation then html), and that stuff is spit on the wire, which as far as it goes is isomorphic to a pipe.

Symbolic computing is a pretty sweet match for FP: some notation for an equation comes in, is turned into an expression tree, then into some standard form, then gets solved or integrated or whatever (recursive tree-walks all over the place), then turned back into a human-friendly form and spit back to the display.

Pandoc is a neat Haskell lib that takes some document format (LaTeX, HTML, markdown, whatever), turns it into a unified representation, then spits it back as another format.

JavaScript is funny because it totally embraces the fact that behavior is data, that pipes are also boxes. You give a callback to a handler, which then puts stuff like it as if were a pipe, and then you have functions that implicitly act on a global, implicit data structure (the DOM), some of them that take a description of where does one want to go in the DOM and returns a node of that recursively-defined tree, such that functions can still be chained and it's relevant to do so even if they're mostly used for their side-effects. JS is the weirdest and most wonderful thing. It even has JSON, which is the funniest example of the code-is-data ethos since LISP and it's exposed AST.

But yeah, it's in the nature of FP to not care about state, so it's normal that keeping state around in FP-styled programs is a pain. Passing a global data structure/container to a function that reaches down into the structure and returns a new, modified instance of that global and re-assigning the new into the old is just... Yuck. If a player gets killed in my game state, I want to just delete it from my player list, not replace my player list with a new one.

It's in the nature of OOP to see behavior as a second-class citizen to state. In patterns parlance, FP means using the iterator and the strategy pattern all over the place as they were themselves objects. Just like OOP lets me put two objects together and have a new one, and perhaps put an array of those inside another object. In FP, I can put two different strategies together, and have another strategy, and put that compound in the iterator pattern/strategy, and have another strategy that I can still compound. Recursion (and the oldest FP performance tweak for it, the tco) is a nice way to express iteration without the use of a dedicated iteration construct, which is totally not a nice piece of pipe; you can do function composition by nesting calls [ much(foo(grunk(one_cookie_yum))); ], but it gets weird [ much(foo(grunk_many_cookies(many_cookies_yum))); but you can't do something like {for one_cookie : many_cookies much(foo(grunk(one_cookie)))}; or you could but it would be a built-in construct which you wouldn't have control over, while in haskell you can just do map (much . foo . grunk) many_cookies ]

So yeah.

If you can picture your program like a pipe, it's probably a good fit for FP. The kind of embedded softare you do probably is not remotely looking like a pipe, so you can keep on not understanding FP. If it can provide you some comfort and perhaps a laugh, the legendary pure-functional no-side-fx-allowed the-ghc-actually-is-the-flagsip-project Haskell has a runtime made out of C.