arr
.map(x => x + 2)
.filter(x => x % 3)
.map(x => other(x))
Imperative? arr
.map(x => x + 2)
.filter(x => x % 3)
.map(x => other(x))
Imperative?Yes, although not necessarily all of them. Some languages will process this eagerly, some lazily.
In C#, the functions being passed could be passed as analyzable syntax trees, and the implementation could actually be in SQL.
Sequencing is a fundamental computing construct. Data flow graphs allow you to specify sequences, just as shell pipelines do. SQL has sequentiality built-in with nested queries. None of these are imperative environments.
Declarative code describes the _output_ or final desired state of something.
Similarly, in mathematical notation, `f(g(x))` can be a way of expressing the existence of some sort of law, e.g. maybe f and g are commutative. That means that if code were written as such in a 5th-gen language[1], the underlying engine is free to recompile the code into `g(f(x))` assuming the commutative property holds and the performance is better. By contrast, in a imperative language, `f(g(x))` generally would compile to that exact order of operations (unless you have a mythical sufficiently smart compiler)
I can see an argument about JIT compilers being smart in some cases, but the philosophical distinction between imperative and declarative paradigms is that with declarative style, the compiler can transparently swap units of arbitrary complexity. For example, given some CSS rules, a browser engine can decide to paint the screen buffer however it wants, be it top-to-bottom, edge-to-center, layer-over-layer, etc regardless of how the CSS was originally expressed.
[1] https://en.wikipedia.org/wiki/Fifth-generation_programming_l...
BTW, pedantry: -2 * -2 = 4 too
Compiled programming languages are declarative ways of generating machine code. The source code describes what the output or final state (the executable) should consist of, but not how to construct it (that's in the compiler source).
Is the code "read x; print x + 5" declarative or imperative?
It's declarative because it doesn't specify how to read the number, how to print the result, or how to add numbers. It merely symbolically describes the IO and calculations to be performed.
It's imperative because it specifies in a step-wise fashion reading input, performing a calculation, and outputting the result.
Declarative code is imperative from the perspective of the next layer up in the abstraction stack. Declarative code elides implementation details; the we call the implementation details imperative, because they specify the "how" and not the "what", which is the domain of the higher level.
Under this lens, what can we say about this:
arr
.map(x => x + 2)
.filter(x => x % 3)
.map(x => other(x))
It's imperative if we understand map() and filter() to be imperative operations. If they're declarative - perfectly possible in C# - then the code is declarative, because `arr` could be quite abstract, and do something much more interesting.So yes—-it is imperative, but it uses functional abstraction to reduce complexity.
1. While each line is a separate step that's done in order, it's done on the results of the previous step. So it's like "get me the sum of the products of the results of foo" rather than "do x, then do y, then do z".
2. Your example has less steps.
It is certainly less imperative than the equivalent C, that's for sure.
Yield an interpretation of a set of boolean variables that satisfies the following conditions:
- A AND B
- (NOT C) or A or B
- A AND (NOT C)
...etc