There are different functional quality layers so to speak.
For one, just having lexically scoped closures as first class citizens doesn't cut it in my eyes. Even though perhaps in some contexts and timelines this would suffice to say that your language (or paradigm) is functional.
In JS using closures was the tool to create modules and objects to maintain internal state. It really was closer to some version of OO than to functional programming, because you would mutate and construct state with these.
Personally I'm not a purist in any sense. I'm all about the interface:
- Does the thing I'm passing you change under me?
- Is the thing you return the same if I give you the same arguments?
Those are the qualities I really care about. In some cases I would even go as far as accepting you violate the former guarantee as long as I'm not using the argument anymore (you own it). What I don't care about is what you do under the hood.
> I don't really understand the aversion to imperative programming.
This kind of functional quality really shines in certain contexts.
For one, a function (in that sense) is easy to reason about and trivial to test.
Secondly, if you think of functions as (lazy) questions about your program state, you avoid memoizing and duplicating state all over the place (as you would with a full-on imperative/OO approach), which can have positive effects on memory pressure, GC etc. You avoid passing around pointers and instead construct normalized and write optimized data structures that can be asked questions when needed.
A thing that follows is ease of concurrency. You now have a better separation of mutations and reads. The concurrency semantics trivially fall out of that.