5 ms·
Looking at the first code sample, I wouldn't call it declarative at all. For me, the defining feature of declarative code, is that it doesn't have a list of act
by cool-RR 6y ago
Looking at the first code sample, I wouldn't call it declarative at all. For me, the defining feature of declarative code, is that it doesn't have a list of actions to be performed one after another. That code sample is such a list of actions, which for me makes it imperative code, meaning "first do this, then do that, then do the other thing."
The "list of actions" approach is what makes code complexity grow exponentially, because whenever you look at a series of 10 instructions, you have to think "what state was created by the combination of the first 7 instructions, and does the eighth instruction depend on that state?"
- Trufa 6y agoSo you would call: arr .map(x => x + 2) .filter(x => x % 3) .map(x => other(x)) Imperative?
- nevir 6y agoYes. That is imperative. Declarative code describes the _output_ or final desired state of something.
- hyperpallium 6y agoIs f(g(x)) imperative? There's an ordering... and it works in terms of mappings, not outputs...
- lhorie 6y agoDepends on what language you're using. In math notation, given `y = x * x`, you can work backwards from `y = 4` to figure out the value of x, whereas in, say, Javascript, `y = x * x` means exactly "compute y as the value of x times itself" and only that. For illustration, we could also compute the square of x in a different imperative form, e.g. in terms of a loop over additions. 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_language https://en.wikipedia.org/wiki/Fifth-generation_programming_l...
- hyperpallium 6y agoI meant in math. BTW, pedantry: -2 * -2 = 4 too
- barrkel 6y agoIt's not so simple. 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.
- deleted 6y ago[deleted]
- cool-RR 6y agoThe border is fuzzy, so there wouldn't be a yes or no answer. Here are a couple of things that make your example more declarative than OP's code: 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.
- lbarrow 6y agoCompared with SQL it's definitely imperative. It's definitely using higher-level abstractions than a `for` loop, but it still specifies the order of operations.
- nicoburns 6y ago> it still specifies the order of operations Yes, although not necessarily all of them. Some languages will process this eagerly, some lazily.
- deleted 6y ago[deleted]
- barrkel 6y agoActually it doesn't unless you're assuming eager evaluation and that 'arr' is something like an array, rather than something which monadically collects functions for evaluation later. In C#, the functions being passed could be passed as analyzable syntax trees, and the implementation could actually be in SQL.
- sriram_malhar 6y agonope. Sequencing and imperative have an overlap, but they are not the same thing. 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.
- TimTheTinker 6y agoYes and no. It’s a functional chain of higher-order operations over a collection. So yes—-it is imperative, but it uses functional abstraction to reduce complexity.
- darksaints 6y agoI would. Because at the pure end imperative->declarative spectrum, you lose turing completeness. It is certainly less imperative than the equivalent C, that's for sure.
- hexane360 6y agoThat's not true at all. The following is a declarative program: 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
- deleted 6y ago[deleted]
- jakelazaroff 6y ago> That code sample is such a list of actions, which for me makes it imperative code, meaning "first do this, then do that, then do the other thing." Animation at its core is a sequence of images. Imperative animation code would describe how those images change every frame. (For example, a for loop in which you multiply properties by i in order to change them over time). Declarative animation code would let you define keyframes — how the images look at specific points in time — and the library would generate the images in between them. (This is called "tweening" [0]). The sample code in this case isn't a list of actions to be performed one after another. It's a list of states (keyframes), which the library interpolates between. That sounds declarative to me! [0] https://en.wikipedia.org/wiki/Inbetweening https://en.wikipedia.org/wiki/Inbetweening
- adamdonahue 6y agoSpot on.
- junke 6y ago> what state was created by the combination of the first 7 instructions, and does the eighth instruction depend on that state? You would still have the same problem by using a notation that looks like function application. The alternative is to repeat all unmodified values, so that looking at each animation step, you know what the output state is.
- hyperpallium 6y agoCan we say the difference between imperative and declarative is ordering or associativity?
- 6gvONxR4sf7o 6y agoImperative and declarative seem to me to be a matter of degree and sometimes even syntax. Point(x=1,y=2,z=3) is pretty declarative, but "point, with x set to 1, with y set to 2, with z set to 3" is getting more imperative even though it's really the exact same thing. But the syntax makes our mental model a little different, so yay. From there, it's not to hard to go to "scope, with x set to scope(a), with y set to x+1, with z set to f(x,y)," which s the same as C-style imperative "{x = a; y = x+1; z = f(x, y)}." There's a reasonably smooth continuum between imperative and declarative. As soon as you introduce lambda functions, declarative gets absurdly flexible and can model stateful computation in a surprisingly ergonomic way, so it's not even a twisted pedantic equivalence.
- Jtsummers 6y agoIt's not really about syntax. It's about the execution model. Your second example might almost feel normal to someone with a preference for SQL, which is a declarative language. Declarative languages don't specify (or minimize the specification of) the execution, imperative languages specify the execution. You can look at the verbs used in describing or verbalizing the language. In declarative languages you don't talk about "assigning" as much as you talk about relationships: "x is y", "x is related to y by f(x,y)", "if x is predicate(x) then y is z else y is z'". In imperative languages you do things: "x is assigned y", "for x in y do ...", "if x is predicate(x) then do y is assigned z else do y is assigned z'". Additionally, statements/expressions in declarative languages can be reordered more freely (the "purer" the declarative language the more true this is), given that it tends towards the relational version. In a constraint based system, for example, you could do these in either order: x in 1..10 x % 3 == 2 ;; => x \in {2, 5, 8} x collects these constraints and so the order is irrelevant (though practically many declarative languages aren't this pure so the order may matter for various reasons).
- sriram_malhar 6y agoSequencing is a fundamental computing construct, not the exclusive preserve of imperative programming. The 'do' notation in Haskell, function composition operators, shell pipes, data flow graphs are all expressions of explicit sequencing. Imperative programming is about each statement altering a program's state, not the act of sequencing.