You bring up a good point, and I've had similar conversations with my friend who is an avid Unity developer, which has a mostly imperative programming (IP) runtime with a declarative GUI.

Since functional programming (FP) and declarative programming (DP) have many similarities, the same issue of hardness (for lack of a better term) exists in languages like Elixer/Clojure/Julia/Haskell/Scala/F# etc etc.

This all comes down to one of the most poorly understood concepts in FP, the monad:

https://en.wikipedia.org/wiki/Monad_(functional_programming)...

Honestly, I'm still not sure I understand internally how monads work, or if there are related terms for how I think of them. But monads (and related devices) are a way to deal with imperative logic and side effects when pure FP can't.

Imagine you're writing Tetris in a spreadsheet. It's easy to visualize how you might enter a piece's (x,y) coordinate in 2 cells and then pipe that through a series of cells containing logic that examines the blocks and outputs the next (x,y) coordinate for the following main loop.

But the actual main loop (that grabs input events) is very hard to visualize as a spreadsheet. This "glue" with the imperative stuff is where the monad comes in. Another way to think of it is, how to represent exceptions in a spreadsheet or a pure functional implementation of a promise chain (you can't without monads).

To write Tetris in HTML/CSS with no Javascript, we'd probably have to write a ton of CSS rules to show/hide/move DOM elements based on the state. That's an intractable problem for humans, so we'd probably compile the CSS from another language, which defeats the whole purpose. Maybe someone has tried it, I can't tell:

https://news.ycombinator.com/item?id=6709466

https://codepen.io/alexdevp/pen/pgdKf

But if we had something like a CSS monad, then we could write rules that are based on dynamic state from other rules. Writing this all out now, something like this might be useful, if anyone knows of such a thing.

Anyway, from what I understand, languages like Clojurescript get around this by suspending execution via a monad and then entering again with new input. Here is an almost purely functional Tetris clone written in Clojurescript:

https://github.com/yogthos/clj-tetris

Piece definitions, with code to offset/rotate the matrices and check for collisions by counting the number of cells to see if any have overlapped:

https://github.com/yogthos/clj-tetris/blob/master/src/tetris...

https://github.com/yogthos/clj-tetris/blob/master/src/tetris...

What I assume to be a thread sleep and JS setTimeout monads:

https://github.com/yogthos/clj-tetris/blob/master/src/tetris...

https://github.com/yogthos/clj-tetris/blob/master/src/tetris...

https://github.com/yogthos/clj-tetris/blob/master/src/tetris...

I know that code looks a little strange. But conceptually it's orders of magnitude simpler than that equivalent imperative code. Note that this is different from easy:

https://medium.com/@kiksdium/simple-vs-easy-bfd897ab293c

TL;DR; you bring up a really good point. FP and DP probably shouldn't be considered proven techniques until they can be used in the same problem domains as IP.