3

On Wikipedia, the article Programming paradigms defines

  • declarative as a paradigm in which the programmer merely declares properties of the desired result, but not how to compute it;
  • imperative as a paradigm in which the programmer instructs the machine how to change its state.

Is assignment declarative or imperative?

For instance,

  • x = 3 in Python;
  • PUT / HTTP/1.1\r\nHost: domain.org\r\n\r\n{"x": 3} in HTTP;
  • UPDATE relation SET value = 3 WHERE key = 'x'; in SQL.
Géry Ogam
  • 602
  • 3
  • 13
  • 1
    +1. Case in point: Would [SSA](https://en.wikipedia.org/wiki/Static_single_assignment_form) be considered declarative or imperative? – rwong Jul 22 '21 at 02:31
  • 3
    Assignment is imperative because it is an act. "X = 3" is not necessarily an assignment though, it can also be a declaration, an informative statement. What it is depends on the context. – Martin Maat Jul 22 '21 at 10:43

5 Answers5

9

Declarative vs imperative programming cannot always be distinguished on a syntactical level. To a large part, this is more about different programming styles: a declarative program will mostly describe the structure of the problem, whereas an imperative program will describe the steps of an algorithm that solves the problem. There is an interesting aspect we must get out of the way first, though: mutability: imperative programming inherently implies that data is modified. In declarative programming, we more often have descriptions of data flows without actual mutability. But while imperative/mutable vs declarative/immutable are correlated, this is not a hard and fast rule to distinguish them.

In your x = 3 Python example, we can't tell what programming paradigm is being used, because that would depend on the wider context. Python as a whole is biased towards imperative programming. It is very statement-oriented. Every statement does something, usually by modifying some state. But an assignment x = 3 could be interpreted as a declarative statement of fact: “in this context, x = 3”, or as an imperative command: “modify the x variable to have the new value 3”.

HTTP is a protocol, and I don't think it makes sense to apply programming language categories to this protocol.

You SQL example is interesting: the UPDATE statement modifies the state of the database, but SQL is widely considered to be a declarative language. In that SQL statement, you don't describe how to update the database. You don't look up indices, you don't perform table scans, you don't acquire locks, you don't update indices. The SQL statement just expresses a problem in this notation for relational algebra. I think this illustrates nicely that you can't always clearly separate these two concepts. A sequence of SQL statements might be imperative (especially when part of a stored procedure), but individual statements are a declarative description of database operations.

amon
  • 132,749
  • 27
  • 279
  • 375
  • By context, you mean the whole program? So if the whole program is `x = 3; y = 2; z = 1` then it is imperative as the execution order (control) is specified, but if the whole program is just `x = 3` then it is declarative as the execution order is not specified? – Géry Ogam Jul 21 '21 at 09:58
  • @Maggyero By context, I mean how the variable is used. There might be data dependencies or orderings even in purely declarative languages. But if a variable is reassigned, that would be an indication that the program follows a more imperative style. – amon Jul 21 '21 at 10:10
  • Interesting, so *assignment* is declarative but *reassignment* is imperative. So the Python program `x = 1; y = 2; z = 3` is declarative but the Python program `x = 1; x = 2` is imperative. – Géry Ogam Jul 21 '21 at 10:21
  • @Maggyero - think of it more as of a spectrum, rather than an either-or kind of thing. Also, you are looking too finely, `x = 1; y = 2; z = 3` is too small and too contrived a program for the distinction to be meaningful. I don't think it makes sense to characterize the generalized concept of assignment itself. Rather, think of it like this: if you have a complex data structure, if client code initializes it though a constructor then that is *more declarative* then if there was no ctor, and the client itself had a number of lines that directly manipulate the data structure to a desired state. – Filip Milovanović Jul 21 '21 at 11:49
  • @Maggyero - in other words, it's about how much the calling code has to (or even can) fiddle with the internals of the thing that it is calling. So `x = 3` can really only be meaningfully called declarative in comparison to the internals of the python implementation (you don't manipulate the low-level internal data representations directly). The other two are declarative in the sense that they are high-level descriptions that encode what you want to happen, and then there's a whole bunch of code at the other end that manipulates all kinds of internal structures to fulfill your request. – Filip Milovanović Jul 21 '21 at 11:49
  • @FilipMilovanović I think that we have at least three possible views on the difference between *declarative* and *imperative*: 1. Your view, which is about the level of abstraction. 2. My view inspired from Robert Kowalski’s paper [‘Algorithm = Logic + Control’](https://www.rose-hulman.edu/class/cs/csse404/schedule/day21/kowalski.pdf), which is about whether logic and control are separated (within the program, or between the program and interpreter) or intertwined (within the program). 3. My other view from the same paper, which is about whether control is in the program or in the interpreter. – Géry Ogam Jul 23 '21 at 19:01
  • @Maggyero These views are all closely related; you can't have the separation of logic and control without introducing an abstraction in between (either by deliberate design, or just by having it emerge from the way you work with the code, perhaps without really being aware of it). The things that enable the separation (the elements that bridge the two: data structures, conventions, operations and their semantics, etc.) *are* the abstraction. So these are all manifestations of the same underlying principle. 1/2 – Filip Milovanović Jul 23 '21 at 19:48
  • @Maggyero The aspect that the declarative/imperative distinction focuses on is whether the code is, in it's *expression* (i.e., when you read it, and try to understand the guiding intent behind it), closer to logic (you can sort of immediately tell by just reading it what it's for) or to control (it's a bunch of statements, control structures, loops, etc., but you can't necessarily tell what is the overall reason the code is doing what it's doing - at least not without studying it more intently, or relying on documentation). 2/2 – Filip Milovanović Jul 23 '21 at 19:48
  • @FilipMilovanović By insisting on the code *expression*, do you mean that declarative/imperative (whether logic and control are separated or intertwined, cf. my definitions 2 and 3 above) is not a feature of the language i.e. of *all* programs, but a feature of *a part of* a program? In other words, that you can switch to declarative or imperative mode in different sections of a program. – Géry Ogam Jul 24 '21 at 11:48
  • The first paragraph of section 1.1 ‘Declarative vs. Imperative Languages’ of Frank Silbermann’s doctoral thesis [*A Denotational Semantics Approach to Functional and Logic Programming*](http://www.cs.unc.edu/techreports/89-030.pdf) gives the following definitions based on Kowalski’s famous analysis ‘algorithm = logic + control’: a *declarative language* has explicit logic and implicit control; an *imperative language* has implicit logic and explicit control. – Géry Ogam Jul 25 '21 at 19:26
5

In a declarative approach, you declare facts, for example that gravity g is 9.80665 m/s2 or the number or wheels x is 3.

Many programming languages express this with some kind of declaration or assignment statement:

let g = 9.80665   // swift
double x = 3;       // c++, java
spaceship(red).  % prolog

In the declarative paradigm you tell the rules and let the system apply these rules to find the result. The rules and facts can be declared in any sequence.

In the imperative paradigm, you have to tell how to use these facts to come to the result; the sequence of operations matters. You may use assignment for other purpose than defining facts: every time you use an assignment that depends on the sequence of statements, and every time you use an assignment to change the previous value of a variable, it is no longer declarative.

Conclusion: assignment can be declarative or imperative, depending on how it is used.

Christophe
  • 74,672
  • 10
  • 115
  • 187
  • Can you be clearer what it means to "declare facts" rather than state "how facts are used"? What even is a "fact" in this context? Even when "declaring", the order of operations must matter when outputs of any stage form the input to another. – Steve Jul 22 '21 at 12:08
  • @Steve I used “facts” as [here](https://wiki.c2.com/?DeclarativeProgramming) i.e. a basic statement that we assume true, without need for any operation/inference/deduction. I avoided a too theoretical debate about whether `freefall_speed is g*t*t/2. %prolog` is a fact or is already a rule ;-) – Christophe Jul 22 '21 at 13:00
  • I'm confused because "facts" are not *assumed* to be true, rather they are articles of knowledge that at some point have been *proven* true. The word for things that are assumed without need for proof (even in principle) is an *axiom* or a *tenet*, not a "fact". I dare say there is a style of language often used in explaining "declarative programming" that doesn't correspond to English as we know it - your own link offers no definition, and uses the word "fact" in ways it immediately condemns as hand-wavy and vague! (1/2) – Steve Jul 22 '21 at 13:26
  • The real point anyway is how do "facts", "rules", "axioms" or whatever, differ from what exists in "imperative" languages (which are the supposed opposite of declarative languages)? As I explain in my own answer, no modern compiler of an imperative language translates the source code slavishly - all of them treat the source code as an exemplary implementation of the desired output or operation, rather than as something that must be followed to the letter. (2/2) – Steve Jul 22 '21 at 13:32
  • @Steve The system has no eyes, and if is has sensors, these are known to be unreliable. This is why I prudently used “assumed to be true”. In fact there are logical systems in which things are not true or false, but something more or less true or false. And there are even truth maintenance systems, that allow to work with a fact until the rules, inferences, term-rewriting leads to a contradiction with other facts; a backtracking then flags the involved facts and rules as potentially questionable. – Christophe Jul 22 '21 at 13:37
  • And of course, compilers of imperative languages may reorder instructions. But they do so based on a graph of value propagation. If you write `g=9.8; t=3; s=g*t*t/2;`, no compiler would start with the last statement (to be honest, with constant propagation, they’d just compute the constant value for s). My point is that intuitively, `int x=3;` could be a declarative statement, whereas `for(int i=0; i<3; i=i+1)` uses an imperative assignment. – Christophe Jul 22 '21 at 13:51
  • But as I say elsewhere, in SQL you can reassign. You seem convinced that you know how to distinguish declarative and imperative, but I'm still failing to follow your train of thought. `int x=3;` appears to me to be C code, and that is counted as an imperative language by any reckoning, yet you say it is a "declarative statement"? – Steve Jul 22 '21 at 14:55
  • @Steve Excuse me being provocative; in reality I’m not convinced of anything in this fuzzy world. It appears anyway that the distinction is difficult, considering that many reference languages for functional programming (in principle declarative) are multi-paradigm. The only purely declarative language that I know is prolog. My main point here is not to define in an unambiguous way the declarative paradigm in three lines but to answer OP’s question, i.e. that assignment can ***express*** a declaration, as well as an imperative command and that it depends on the ***intent***. – Christophe Jul 22 '21 at 15:28
  • Thanks Chris! Your view on the difference between *declarative* and *imperative* seems to match one of the two possible views that I inferred today from Robert Kowalski’s paper [‘Algorithm = Logic + Control’](https://www.rose-hulman.edu/class/cs/csse404/schedule/day21/kowalski.pdf) (he does not define the two terms so I am only speculating): 1. The difference is about whether logic and control are separated (within the program, or between the program and interpreter) or intertwined (within the program). 2. The difference is about whether control is in the program or in the interpreter. – Géry Ogam Jul 23 '21 at 21:29
  • @Maggyero This is indeed an interesting thought! The declarative style could facilitate the expression of the logic whereas the control part is left to the language implementation. The example of factorial in a functional language expressed using pattern matching would fully correspond to the logic described in this article. Moreover this gives a framework for hybrid programming, when a functional style is adopted in an imperative language, but still some control is required to assemble the parts/exploit the logic. – Christophe Jul 23 '21 at 22:43
  • @Maggyero Nevertheless some caution is required, since declarative approach could also be used to express intermediary requirements/conditions that are not related to the problem logic but to a specific way of implementing it. For example edges of a graph could be declared as a matrix, as a list of paired vertices, as a list of target vertices for a given vertex, etc…: none of these describe the underlying abstract graph. So it’s not a 1:1 mapping; at best an overlapping. – Christophe Jul 23 '21 at 22:54
  • I think you are referring to Kowalski’s advice of separating data structures from procedures that operate on them (i.e. procedures should operate on abstract data structures, and their representations should be provided separately, but still in the logic component). – Géry Ogam Jul 23 '21 at 23:02
  • About assignment, since it is a predicate in a clause of the program (isn’t it?), it does not affect whether the language is declarative or imperative, which has to do with who is responsible for how the clauses are used (independently of the nature of the clause). So I agree with your conclusion that assignment can be either declarative or imperative depending on how it is used. But I don’t understand this: ‘every time you use an assignment that depends on the sequence of statements, and every time you use an assignment to change the previous value of a variable, it is no longer declarative.’ – Géry Ogam Jul 23 '21 at 23:16
  • If the interpreter provides the control component for instance, the language is by our definitions (1 and 2) declarative. So why would a clause with an assignment that depends on the sequence of statements or that changes the previous value of a variable no longer be declarative? (Since with our definitions, declarative/imperative is a language feature, not a clause feature.) – Géry Ogam Jul 23 '21 at 23:21
  • @Maggyero I fear there is a shortcut in this reasoning, based on analogy, i.e. between logic and declaration, and how control is done. As said, I think there could be some overlap, but that this was not necessarily the same thing. Moreover a declaration could also be a pure function that does not change any declared value and does not have any side effect. Or even a higher order function that returns a function. This puts in question separation of data and control. If we take some functional languages, you could express the full program (domain logic + internal logic) – Christophe Jul 23 '21 at 23:35
  • whereas you could express it partly using a declarative approach and partly using an imperative style. It’s the same language, the same compiler/interpreter/environment, but different paradigms. Do for me it’s still a question of expression of some intent. – Christophe Jul 23 '21 at 23:37
  • Do you mean that declarative/imperative (whether logic and control are separated or intertwined, cf. my definitions 1 and 2) is not a feature of the language i.e. of *all* programs, but a feature of *a part of* a program? In other words, that you can switch to declarative or imperative mode in different sections of a program. – Géry Ogam Jul 24 '21 at 11:28
  • 1
    @Maggyero I’m not a master of functional programming, but some experimentations with Ocaml suggest that yes. As stated in an exchange with steve above, many functional programming language, which should work according to declarative paradigm, are nowadays multiparadigm. – Christophe Jul 24 '21 at 11:35
  • Okay, so that is the sense of your conclusion ‘assignment can be declarative or imperative, depending on how it is used’: an assignment used in a declarative section of the program text (i.e. where logic and control are separated) is declarative, and an assignment used in an imperative section of the program text (i.e. where logic and control are intertwined) is imperative, isn’t it? – Géry Ogam Jul 24 '21 at 11:41
  • @Maggyero I think this is what I meant – Christophe Jul 24 '21 at 11:46
  • So far we have several competing definitions for the declarative/imperative distinction: DanielT’s *permanent relation/transient relation*, JacquesB’s *immutable state/mutable state*, Steve’s *higher-level language/machine language*, and Christophe’s *separated logic and control/intertwined logic and control*. – Géry Ogam Jul 24 '21 at 13:02
  • @Maggyero it was not clear to me that your question intended to distinguish between declarative and imperative. I thought you were questioning the nature of an assignment. – Christophe Jul 24 '21 at 13:53
  • I was actually questioning both. – Géry Ogam Jul 24 '21 at 14:05
  • @Maggyero I just read Daniel’s answer and I confirm that we have the same definition. In fact permanence is related to the absence of change, and the changed are related in my answer to imperative paradigm. In imperative you have transient variable values, and the last known state is used when any of the computations are performed. This is why sequence matters. In a declarative world, you cannot change facts. If there are time dependent facts, you would declare the value in relation with the time at which it applies (e.g. speed(t) with speed(0)=0; speed(9)=100;). – Christophe Jul 24 '21 at 14:31
  • Okay, but if you share Daniel’s *permanent relation/transient relation* definition then you don’t share my *separated logic and control/intertwined logic and control* definition. Because the latter is not about the prohibition of state changes, it is about the isolation of control. And I have just answered to Daniel that his definition might be artificial (though I am not sure) because it depends on language semantics. – Géry Ogam Jul 24 '21 at 14:37
  • @Maggyero maybe I misunderstood your statement in which I claimed to agree with your categorization of my answer. Re-read carefully my comments above in which I find the logic/control view interesting but at the same time called for some caution and observed a partial overlap (“at best”) of logic and declarative. – Christophe Jul 24 '21 at 14:54
  • So under your *permanent relation/transient relation* definition, if you add new facts but do not change existing ones then it is declarative, otherwise it is imperative? E.g. in Python, the program `x = 1; y = 2` is declarative and the program `x = 1; x = 2` is imperative. In SQL, the program `CREATE TABLE r (x INT); INSERT INTO r VALUES (1); INSERT INTO r VALUES (2);` is declarative and the program `CREATE TABLE r (x INT); INSERT INTO r VALUES (1); UPDATE r SET x = 2 WHERE x = 1;` is imperative. In CSS, the program `h1 { font-size: 1.5em; color: blue; }` is declarative. – Géry Ogam Jul 24 '21 at 16:50
  • @Maggyero Let’s say that the assignment statements in x=1; y=2; are purely declarative (assuming x and y were not bound before), whereas the assignment statements x=1; x=2; are imperative. If that’s all the programme, you could extent the qualification to the whole programme. But the programme would be pretty useless. And useless declarative vs useless imperative does not matter. What counts is how the problem is going to be solved. And here, python is not a declarative language, so you’d end up with a couple of declarations but a lot of imperative procedural programming. Try f# ? – Christophe Jul 24 '21 at 17:23
  • Let’s say that the problem is to compute the factorial of 4. In Horn clauses, you write only the logic (no control): Fact(0, 1); Fact(n, x) if Minus(n, 1, y) and Fact(y, z) and Times(n, z, x); Fact(4, x). In Prolog, you write the exact same logic (no control). In Python, you write the logic with the control strategy, for instance *top-down* (from the goal statement to the fact assertions): `def fact(n): return 1 if n < 2 else n * fact(n - 1)\nfact(4)`, or *bottom-up* (from the fact assertions to the goal statement): `def fact(n):\n r = 1\n for i in range(n): r *= i + 1\n return r\nfact(4)`. – Géry Ogam Jul 24 '21 at 19:23
  • So your *permanent relation/transient relation* definition of declarative/imperative seems equivalent to *top-down control/bottom-up control*, since the top-down Python program above does not use assignment in its recursion so is classified as declarative, and the bottom-up Python program above does use assignment in its iteration so is classified as imperative. Which is different from my *separated logic and control/intertwined logic and control* definition which classifies Horn clauses and Prolog as declarative and Python as imperative. – Géry Ogam Jul 24 '21 at 21:45
  • The difficulty with declarative/imperative is that *predicate logic* (which is declarative) can be interpreted as a *programming language* (which is imperative) for commanding a machine, thanks to the procedural interpretation discovered by Robert Kowalski, published in his famous 1974 paper [‘Predicate Logic as Programming Language’](http://www-public.imtbs-tsp.eu/~gibson/Teaching/CSC4504/ReadingMaterial/Kowalski74.pdf) and implemented in Prolog. Since a declarative language can be interpreted as an imperative language, how are they different? Is it the addition of control to the logic? – Géry Ogam Jul 25 '21 at 08:52
  • The first paragraph of section 1.1 ‘Declarative vs. Imperative Languages’ of Frank Silbermann’s doctoral thesis [*A Denotational Semantics Approach to Functional and Logic Programming*](http://www.cs.unc.edu/techreports/89-030.pdf) gives the following definitions based on Kowalski’s famous analysis ‘algorithm = logic + control’: a *declarative language* has explicit logic and implicit control; an *imperative language* has implicit logic and explicit control. – Géry Ogam Jul 25 '21 at 19:22
  • @Maggyero I like those exchanges with you because (as already demonstrated in previous questions) you are very knowledgeable and have interesting references. Again these arguments in a PHD thesis are interesting thoughts. I suspect it to be a provocative simplifying statement, especially as many original functional languages ended up multi-paradigm. However, with all due respect, this site is meant for specific and focused questions, and not for a debate of (informed) opinions that would require to develop arguments as long as such a phd thesis ;-) – Christophe Jul 25 '21 at 20:29
2

First we need to clarify the distinction between "declarative" and "imperative".

Be aware that there is no single canonical definition of "declarative" in computer science, so there is a certain level of subjective opinion and a lot of grey area. But a reasonable definition is:

In a declarative language the code is a specification of the desired end result. In an imperative language the code specifies a sequence of operations executed over time (hopefully resulting in the desired end result).

It follows from this that imperative languages have a notion of mutable state. Each operation change mutable state or have some other side effect (like generating output).

Declarative languages describes a single state and does not have any explicit notion of sequential execution or mutable state.

This brings us to assignment. Assignment can really have two meanings. In an imperative language like Python, assignment sets a mutable variable to a particular state. A variable can have multiple states over time (hence the name).

Declarative and functional languages can also have assignment, but these are more like aliasing. It binds a name to a particular value or expression in a given context. But the binding does not change over time. Assignment in declarative or functional languages are therefore more often called declarations or bindings rather then assignments.

This distinction may be subtle, but consider that in an imperative language like Python, a name like 'foo' might refer to different variables in different scopes and each variable may change value over time. In a declarative language, the same name may refer to different binding in different scopes, but a binding never change over time.

For example CSS is a declarative language and CSS variables are assigned a value, but they cannot change at runtime (which means the name "variable" is really a misnomer - it is a constant declaration rather than a variable).

Update in SQL is imperative since they change state. Base tables are sometimes called "relation variables" (or "relvars") to indicate they are mutable over time. In contrast, the query syntax in SQL is declarative.

The imperative/declarative distinction does not apply to protocols like HTTP. Here we would instead talk about side effects or not. PUT has a side effect.

JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • Thanks! In other words, as amon said, reassignment is imperative. So in SQL `UPDATE` is imperative, while `INSERT` is declarative, right? – Géry Ogam Jul 21 '21 at 10:44
  • INSERT is also imperative since it changes state. SELECT is declarative. – JacquesB Jul 21 '21 at 10:47
  • Well contrary to UPDATE, INSERT does not really *change* state, it *creates* state. – Géry Ogam Jul 21 '21 at 11:19
  • @Maggyero: INSERT certainly changes state, since a query may give a different result after an insert. – JacquesB Jul 21 '21 at 12:31
  • 1
    I agree for INSERT. ‘CSS variables are assigned a value, but they cannot change value over time’ Yes and I think that the fundamental reason is that a CSS style sheet represents a *single* final state, not *multiple* intermediary states. – Géry Ogam Jul 21 '21 at 18:00
  • What do you think of the following definitions? A *statement* expresses a property of the final state, like its value (e.g. `x, y = 1, 2` in Python, `UPDATE relation SET value = 1 WHERE key = 'x';` in SQL, `h1 { font-size: 1.5rem; }` in CSS, ` x` in HTML, `sqrt(x) where x is a non-negative real is the non-negative real y such that y^2 = x.` in English). – Géry Ogam Jul 21 '21 at 18:45
  • A *command* expresses properties of a sequence of states, like their values (e.g. `x = 1; y = 2` in Python, `UPDATE relation SET value = 1 WHERE key = 'x'; UPDATE relation SET value = 2 WHERE key = 'y';` in SQL, `sqrt(x) where x is a non-negative real is approximated by Newton’s method with y -> y^2 - x on non-negative reals.` in English). – Géry Ogam Jul 21 '21 at 18:54
  • *"the name "variable" is really a misnomer"* - not in the sense that the programmer can vary the value assigned to a given name, and part of the purpose of using names is to allow the values to be easily varied. Obviously some languages distinguish run-time variables from source code variables, and call the latter "named constants", but in CSS the absence of run-time variables makes the notion of "constants" unnecessary. Also, it is possible to replace the values assigned to field names in SQL queries, in a manner that is indistinguishable from assignment in any other language. – Steve Jul 22 '21 at 12:34
  • 1
    Isn’t declarative vs. imperative more about intent? If you insert a new key/value pair, you change a table, but you could nevertheless just declare in that table some new knowledge. You could even update a table with a value for key that had none (null in SQL semantic) and still be in a declarative paradigm. In a purely declarative language you could also declare new facts that change the internal state of the execution, without being accused of imperatism ;-) (1/2) – Christophe Jul 22 '21 at 14:19
  • Of course, if you update a key with an existing value, with a new value, you are no longer declarative: you change something and all the other computations you may do, may depend on whether you made them before or after that change (assignment). If you would like to do this in a declarative paradigm, you’d need to extend the key with a “time” column. Or you’d write in some hypothetical language x(t0)=3; x(t1)=4, and the system could compute a value for t0 and t1: all declarations stay valid, but some may interest you more – Christophe Jul 22 '21 at 14:22
  • @Steve: Are you thinking of SQL projection like `SELECT foo AS bar ...` `? This does not change the name of the column, it just define the name used in the result set. The column in the base table still has the original name. – JacquesB Jul 22 '21 at 18:13
  • @JaquesB, indeed, but a more pertinent example would be something like `SELECT ISNULL(foo, ' ') AS foo`. The point here is not that the base table is overwritten, but that the value referred to as "foo" in the local context of the query can be changed - nested queries or with-clauses obviously make it possible to apply a chain of select-clauses where the same name can be repeatedly rebound to different values at each stage of the pipeline. I struggle to see how this isn't a form of multiple assignment? – Steve Jul 22 '21 at 21:21
  • @Christophe ‘In a purely declarative language you could also declare new facts that change the internal state of the execution, without being accused of imperatism ;-) Of course, if you update a key with an existing value, with a new value, you are no longer declarative: you change something and all the other computations you may do, may depend on whether you made them before or after that change (assignment).’ Do you still defend the view that declarative/imperative is about immutability/mutability? It is very different from the view that it is about logic and control separation/intertwining. – Géry Ogam Jul 24 '21 at 12:05
  • @Steve: Aliasing an expression with a name is different from assigning a value to a mutable variable. Assigning a value to a mutable variable is an imperative operation since it changes state. A name binding in a declarative language does not change any state, it just introduces an alias in a given context. – JacquesB Jul 24 '21 at 13:11
  • @JacquesB, I anticipated this. We could say are shadowing the previous field of the same name rather than overwriting it, but you could equally conceive assignment in an imperative language in the same way (so in `For i = 1 To 5`, we could say we are actually referencing a different `i` on each iteration, rather than the same `i` being changed). It's a difference without a distinction, except the free choice of the programmer about whether to attribute a continuous existence to one named reference whose value changes, or to conceive it as a series of different references of the same name. – Steve Jul 24 '21 at 18:04
  • The first paragraph of section 1.1 ‘Declarative vs. Imperative Languages’ of Frank Silbermann’s doctoral thesis [*A Denotational Semantics Approach to Functional and Logic Programming*](http://www.cs.unc.edu/techreports/89-030.pdf) gives the following definitions based on Kowalski’s famous analysis ‘algorithm = logic + control’: a *declarative language* has explicit logic and implicit control; an *imperative language* has implicit logic and explicit control. – Géry Ogam Jul 25 '21 at 19:24
  • @Steve: Haskell does something like this in its do-notation syntactic sugar. It basically allows you to write code in an imperative style, even though the underlying semantics are purely functional. The left arrow `foo <- bar` looks like mutable assignment, but transforms into a binding in a new scope. – JacquesB Jul 26 '21 at 13:14
  • @JaquesB, so now we even have languages that are 'declarative in an imperative style'? As an aside, I wonder whether what you're grasping at is the distinction between "declarative" languages that claim to conform in some substantial way to mathematical tenets and the mental models of mathematicians, that they are mathematically rigorous in some way, and "imperative" languages that make no such claims? And it's possible to interpret a declarative language in an imperative fashion, but there can be features of imperative languages that cannot be interpreted declaratively. – Steve Jul 26 '21 at 19:45
  • @Steve: Lots of languages are declarative without making any claim of being "mathematically rigorous". For example HTML is declarative but makes no such claims as far as I know. – JacquesB Jul 26 '21 at 20:06
  • @JaquesB, that's an interesting counter-example. Does HTML meet the criteria of a programming language though? It is simply structured data - you cannot specify operators or any computation. If it did, then it would surely cease to be declarative in this sense. I'm willing to accept that HTML is declarative in the sense of "consisting of no computation". – Steve Jul 27 '21 at 13:54
  • @Steve: CSS is another declarative language and do contain operators and computations e.g using calc: https://developer.mozilla.org/en-US/docs/Web/CSS/calc(). – JacquesB Jul 27 '21 at 16:32
  • @JaquesB, I'm not that familiar with CSS, but as before I question whether this is a *programming* language in the proper sense? Secondly, at the statement level, and given that CSS can define simple formulas (but which are not the main feature of the language), then we're simply back to the issue broached elsewhere about whether expressions are declarative or imperative in nature, and also back to the issue above about whether one of the very definitions of "declarative" is broadly 'anything that has a mathematical flavour' (which expressions do). – Steve Jul 27 '21 at 23:23
  • @Steve: Surely depends on what you mean with "in the proper sense". Declarative domain-specific languages like HTML or CSS or SVG are typically not considered programming languages. But the distinction is largely arbitrary since for example XSLT typically *is* considered a programming language despite also being a declarative domain-specific language. – JacquesB Jul 28 '21 at 11:14
  • @JaquesB, I'm not intimately familiar with XSLT (I think I may have used it briefly in the distant past), but it strikes me basically as a query language, and thus it simply raises the question about what makes query languages declarative. I do feel as though we are about to go around in a circle here, because additional languages are being adduced without any explanation of what makes them declarative. I'm doing my best to guess what the perceived distinction may be (which is the real question here), but by contrast you simply state that it is a declarative language in a just-so fashion. – Steve Jul 28 '21 at 18:56
  • @Steve: In a declarative language the code is a specification of the desired end result. In an imperative language the code specifies a sequence of operations executed over time, operating on mutable state. – JacquesB Jul 28 '21 at 21:53
  • @JaquesB, but as I've said before (somewhere on this question), those two statements are objectively equivalent with one another. Imperative code "specifies the desired end result" of each step (and can consist of specifying high-level steps), and declarative code invariably "specifies a sequence of operations" (even if the order of evaluation differs from the order of appearance in source code). I don't want to press you further on this though, if you can't add to what you've already said. – Steve Jul 29 '21 at 01:26
  • @Steve: In what sense does say CSS specify a sequence of operations? – JacquesB Jul 29 '21 at 07:23
  • @JaquesB, overall, CSS isn't a sequence of operations, because it's largely a static list of independent constants or settings, and doesn't express (or imply) any computation. So far as it admits simple expressions and named references however, like all expressions they *do* have a defined order of evaluation, and most imperative languages have some facility for expressions. Although I wouldn't judge a language like CSS wholly on its peripheral features, in my view expressions (at least as I know them, in a computing context) are an imperative feature. – Steve Jul 29 '21 at 08:57
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/128017/discussion-between-jacquesb-and-steve). – JacquesB Jul 29 '21 at 11:12
2

Imperative vs Declarative Programming

Many of the answers here (and the question itself for that matter) fall into the old trap of the "what vs how" metaphor. I think it's the wrong way to look at the question. It doesn't tell us if x = 3 is declarative or imperative. It doesn't tell us about a = b + c...

So let’s take this simple code a = b + c as a basis and view the statement in a few different languages to get the idea:

When we write a = b + c in an imperative language, like C, we are assigning the current value of b + c to the variable a and nothing more. We aren’t making any fundamental statement about what a is. Rather, we are simply executing a step in a process.

When we write a = b + c in a declarative language we are asserting a relationship between a, b and c such that it is always the case that a is the sum of the other two. It’s not a step in a process, it’s an invariant, a guarantee, a declaration of truth.

In essence, declarative code doesn't "do anything," it merely states facts. Imperative code is required otherwise nothing would ever get done. Any language allows you to write in either style.

So when examining code to determine its style don't think "what vs how." Instead ask yourself, "does this code express a fact that is always true regardless of the state of the system, or does it update the state of the system?

Daniel T.
  • 3,013
  • 19
  • 24
  • Thanks Daniel. It is funny that you answer here because I read your blog article yesterday. So you define the declarative/imperative distinction in terms of *permanent relation/transient relation*. But cannot facts be changed during computation in a declarative language? Competing definitions are JacquesB’s *immutable state/mutable state*, Steve’s *higher-level language/machine language*, and Christophe’s *separated logic and control/intertwined logic and control*. – Géry Ogam Jul 24 '21 at 12:56
  • 1
    @Maggyero Christophe's definition and mine are the same. Logic, by its nature, states facts that are true regardless of state. IE, logic describes invariants. And JacquesB's notion is a sub-type of mine and Christophe's. As I mentioned in my article, stateless execution is necessarily declarative as well. Only Steve's definition is at odds with the rest and it's because IMO, he's looking at the historical definition of the word, not its current usage. – Daniel T. Jul 24 '21 at 13:08
  • @DanielT. I also think that we have the same definition, and you have a nice way to express it. Indeed, in my examples, I have carefully avoided a=b+c, since this would be an assignment in an imperative language (deriving in a controlled manner new facts from existing facts) , and would be a rule in purely declarative languages (i.e. making a general statement regardless of b and c if those are not yet known, or regardless any 2 of the 3 variables with a decent inference engine). And the general rule has in many languages a very different syntax, not using assignment, which was the topic. – Christophe Jul 24 '21 at 14:15
  • But an assignment is interpreted in denotational semantics as a permanent relation between two states, i.e. a logic clause. So doesn’t this make your *permanent relation/transient relation* definition artificial? Christophe’s *separated logic and control/intertwined logic and control* definition does not have this issue as it makes a distinction between logic and control i.e. is independent of language semantics. – Géry Ogam Jul 24 '21 at 14:25
  • 1
    *"It’s not a step in a process, it’s an invariant, a guarantee, a declaration of truth."* - but what is the difference between the two conceptions? I can take any plainly imperative code, and insist that it isn't actually a computational step, but a declaration of truth and a relationship that must hold. Yet the whole point of expressing the "relationship" is to define a necessary step of computation. Is there an example of relations that don't involved steps? Why would a programmer even specify a relationship, if it didn't imply a step in the computation? – Steve Jul 24 '21 at 18:52
  • The expression 1+1 = 2 is not a step in a process. The formulas you put in spreadsheet cells are not steps in a process. Haskell code that doesn't use the IO monad is another example. That said, one could argue that a program file itself, independent of the interpreter that runs it, is a "declaration" of some sort. At the lowest level, all code is imperative; it's just moving data in and out of registers. These declarative facts are something we lay on top of that underlying imperative layer. – Daniel T. Jul 24 '21 at 19:42
  • In other words, if all you are doing in the code is stating facts, then nothing will ever get done. That's why every language, even SQL, has an imperative aspect to it. – Daniel T. Jul 24 '21 at 19:53
  • @DanielT, to my eye, `1+1=2` is a step in a process - it is a series of operators and operands with a defined order of evaluation, namely an addition and then a comparison. You also use the word "fact" which I addressed in comments on Christophe's answer. What exactly is it that causes you to say the above expression is not imperative or a "step in a process", when it clearly specifies a series of computational steps? – Steve Jul 24 '21 at 21:51
  • Does it really represent a series of computational steps Steve? Put `1+1==2` in a C program and examine the asm code that is generated. I strongly suspect that no computational operations would be generated at all. Maybe we have a different definition of what "computational operation" means. – Daniel T. Jul 25 '21 at 00:45
  • @DanielT, but then you're invoking my explanation about the presence of an optimising compiler, and casting C as declarative because it has an optimising compiler capable of radically overriding the instructions stated in the source code (so long as the output would be logically equivalent). – Steve Jul 25 '21 at 10:02
  • 1
    The first paragraph of section 1.1 ‘Declarative vs. Imperative Languages’ of Frank Silbermann’s doctoral thesis [*A Denotational Semantics Approach to Functional and Logic Programming*](http://www.cs.unc.edu/techreports/89-030.pdf) gives the following definitions based on Kowalski’s famous analysis ‘algorithm = logic + control’: a *declarative language* has explicit logic and implicit control; an *imperative language* has implicit logic and explicit control. – Géry Ogam Jul 25 '21 at 19:24
0

In my view, the imperative/declarative distinction does not exist in the way most people seem to describe it.

What does exist is what nowadays would be recognised as the absence or presence of an "optimising compiler".

SQL is usually considered the canonical example of a "declarative" language. It was conceived during an era when assembly languages still reigned - where every CPU instruction was painstakingly specified.

By contrast to assembly, SQL bears no relation to specifying CPU instructions, and instead the basic work is done by employing a small set of array-oriented operators, and the database engine can fulfil an SQL query using a choice of multiple algorithms and strategies that bear no obvious relation to what the programmer wrote (and yet are provably equivalent).

In this context decades ago, practitioners knew what they meant by the imperative/declarative distinction, even if they don't seem to have bequeathed us with a sound explanation of it.

They meant the fundamental difference between assembly language where you specify every single CPU instruction, and something as comparatively wild as SQL where you write a few lines manipulating an abstract representation of data, and the compiler sort of works out the CPU instructions for itself.

Today however, assembly has long fallen by the wayside, and compilers for all mainstream languages can produce CPU instructions that don't relate in any simple way to the source code. Many languages run in their own VMs. Effectively all mainstream languages are now "declarative" to some significant degree.

SQL remains unique in the mainstream for the sheer breadth of optimisations available to the "compiler" (the database engine), since the entire technology was designed precisely for this purpose from the outset, and has had decades to mature. But it is only a question of degree in this respect, not of fundamental difference.

That said, the question of whether "assignment is declarative or imperative" simply falls away as meaningless, since all languages have some concept of assignment.

Steve
  • 6,998
  • 1
  • 14
  • 24
  • This is an interesting answer. But isn’t the presence of an optimizing compiler an obsolete criteria since the emergence of just in time compiling and bytecode, that would allow to compile anything when sufficient information is known, not to speak about transpilers? Haven’t many functional languages that use declarative paradigm (e.g Ocaml, f#) also their optimizing compiler? Even [Prolog](https://en.m.wikipedia.org/wiki/Prolog) a declarative logic programming language has an optimizing compiler. – Christophe Jul 22 '21 at 14:07
  • I just see that JacquesB has solid arguments about SQL being an imperative language – Christophe Jul 22 '21 at 14:09
  • @Christophe, my point is precisely that the "optimising compiler" is what people a generation ago meant by "declarative". Effectively everything is now declarative, because declarative (in accordance with my argument) means that source code statements don't translate directly into a fixed set of machine instructions, but instead are manipulated by an optimising compiler. The usefulness of this distinction thus no longer exists, because the ideas and features which were specific to declarative languages decades ago, are actually now characteristic of every mainstream language. – Steve Jul 22 '21 at 15:07
  • Also, I wouldn't say @JaquesB has "solid arguments" about SQL being imperative. He more or less just baldly asserts that it is the case, and appears not to realise that the "query syntax" (which I gather means a select statement) is also capable of reassigning values to names - otherwise every time you created a calculated column, you'd have to give the output column a different name than the input column, which often isn't desired at all. – Steve Jul 22 '21 at 15:15
  • I think this is a good answer regarding the history of the word "declarative" in the software engineering context. However, the meaning of words can change over time. I think this particular word's definition has changed quite a bit over the last 47 years. – Daniel T. Jul 24 '21 at 12:30
  • So far we have several competing definitions for the declarative/imperative distinction: DanielT’s *permanent relation/transient relation*, JacquesB’s *immutable state/mutable state*, Steve’s *higher-level language/machine language*, and Christophe’s *separated logic and control/intertwined logic and control*. – Géry Ogam Jul 24 '21 at 12:59
  • I don't think the definition of declarative as just meaning "high level" is shared by anyone. Python is super high level, but I have never seen any reasonable person characterize Python as "declarative". Declarative languages tend to be higher level, but that does not not mean all higher level languages are declarative. – JacquesB Jul 24 '21 at 14:41
  • @DanielT, certainly the definition can change, and I wouldn't mind if someone could actually give a coherent and widely-accepted definition of the "new" concept. – Steve Jul 24 '21 at 18:28
  • @Maggyero, the point is not quite just the distinction between high-level language and machine language (otherwise declarative/imperative would just be a synonym for high/low level), it's whether the source code goes through a relatively trivial transformation into machine language, or whether it passes through a compiler which has a wide latitude for optimising, rearranging, even simplifying or eliding. Because these techniques have been adopted in the compilers of all languages, I argue the distinction has ceased to be useful. – Steve Jul 24 '21 at 18:42
  • The first paragraph of section 1.1 ‘Declarative vs. Imperative Languages’ of Frank Silbermann’s doctoral thesis [*A Denotational Semantics Approach to Functional and Logic Programming*](http://www.cs.unc.edu/techreports/89-030.pdf) gives the following definitions based on Kowalski’s famous analysis ‘algorithm = logic + control’: a *declarative language* has explicit logic and implicit control; an *imperative language* has implicit logic and explicit control. – Géry Ogam Jul 25 '21 at 19:24
  • @Maggyero, I think what I find confusing is that, as a programmer, I don't write either logic or control alone. I write algorithms, and I write them using programming languages. Kowalski seems to define these two things as parts of an algorithm, not as items that on their own can comprise a programming language. If the language alone isn't fully explicit about the steps of the algorithm (as many aren't), then I employ a canonical algorithm (using my preconceived understanding of the language and it's operators), which is what allows me to understand the language. (1/2) – Steve Jul 25 '21 at 22:41
  • I find it very hard to understand how a programmer would know what code *does*, if he wasn't in fact consciously aware of an algorithm that implements the "logic". Whilst the same logic could comprise different algorithms (because those algorithms could have varying control elements which differentiate them), it doesn't seem possible to specify the logic in isolation without it being integrated into an algorithm, because it would be like the grin without the cat. (2/2) – Steve Jul 25 '21 at 22:41