In declarative systems, you want to explain the final end-state.
Often your requirements on the final end-state involve some predicate logic. I want (A and B) or C. Using an Or makes sense, if you want your declarative logic to work on both windows and linux. Or you want configurable declarative logic, or you want to be easily able to manually change the files.
A very natural way to do predicate logic is "if A is true, then B should be true". That is logically equivalent to "B or not A". But letting people write "if A, then B" is just nicer.
Hence declarative logic is much nicer with "if statements".
Perhaps another way to view this is that many declarative systems "build" their description of the desired end-state imperatively. I wouldn't say that applies here, but I could imagine cases where you want to say 'for all elements here' which might be best solved with a for loop.
I understand. But as you seem to hint at, it seems that "declarative" logic is still an imperative concern due to insufficient specification of the declarative framework.
Which is fine in some cases. It is still control flow at a somewhat meta level.
But taking the example of a declarative definition of a UI tree, changing node identity using "declarative" if statements seems like a breach or even a smell from a declarative point of view.
The same way, for eachs are not necessary in the declarative layer. That can just be array iteration in the imperative/data layer.
I think the main difference is that, in a declarative statement like:
x = (if c then a else b)
it is guaranteed that only x is affected. With imperative programming, you have to look inside the conditional to find what is affected.
if c then:
x = a
else:
y = a
You could still make the above snippet work in a declarative way but, as I say, the targets are explicit
x, y = (if c then a, y else x, a)
If you have function calls involved then the problem is even worse. In an imperative language, the following code could mutate x, y and any other variables arbitrarily (if the scoping rules allow it):
x = 0
if c then:
f()
Whether this difference is enough to describe "if" as not "control flow" in declarative languages, I don't know, but that is not really an interesting question (just a matter of how you define the term).
Edit: another difference is that I think declarative languages usually only use immutable types, so changing one variable is guaranteed not to affect another. Contrast with this Python snippet, where mutating x affects y.
Plenty of good answers already, but I’ll add this to clarify:
> A declarative if statement doesn't make sense.
That’s exactly right (and was the point I was trying to make, though I can see how it isn’t necessarily clear, particularly with the pseudo code syntax I chose). But a declarative if expression definitely makes sense.
Let me rewrite the example as an s-expression:
(if condition
a
b)
In a lisp, this would produce a value which is either a or b depending on the value of condition. (It would typically be implemented as a macro so only a or b is evaluated, but that’s an implementation detail.)
But an if expression is not the only way to declare conditions. For instance you might have something like:
(first
(filter predicate?
[a b]))
This isn’t exactly semantically equivalent, but it’s hopefully clear that it’s not imperative.
Of course lisps are generally (though not always) closer to the functional paradigm than a more declarative paradigm like say logic programming. But if you look at say Prolog, you’ll find that it too uses conditions for definition (though again with somewhat different semantics).
It’s an “operation” in the mathematics sense, but it’s not an operation in the imperative sense. It doesn’t do anything (ie it doesn’t have an effect), it is part of the description.
Let’s step back from “if” or “filter”. Suppose you have a basic primitive definition “book”. If you’re to define a taxonomy of types of books, you might start with “fiction” (or “non-fiction”, but this terminology kinda gives away where I’m heading). Okay, so a definition of a book which is fiction might be:
fiction-book:
book &
factual-constraints: none
So far we have entirely static definitions (at least within a universe that book-like things need no distinction). We don’t even have to define what could be assigned to factual-constraints.
What happens when we try to define non-fiction? We have to define factual constraints it might have.
non-fiction-book:
book &
factual-constraints: all
There are some implied conditions here, and we could just stop there. If you have a book and want to know whether it is non-fiction, you must evaluate its non-presence in sets that don’t conform to full veracity (let’s be generous here and pretend non-fiction is honest, and omniscient).
But let’s go further, and define so-called “hard science fiction” books (ie there are aspects which might be imaginary but only if the imaginary aspects are conceivable without disputing known facts, I know this is imperfect but I think it’s good enough for the purpose of this discussion):
hard-scifi-book:
book &
factual-constraints:
all - known-impossible
Uh oh, that’s an operation! Have we started imperatively defining types of books? Nope. We’ve declared how they’re defined, without specifying their inputs, and without affecting them. To the extent the definition itself is correct (exceptions already acknowledged), the definition will always be correct because it’s a tautology.
But you can’t have the definition without those conditions, because a definition is a tautology. And that is the tautological definition of a definition.
What would make it imperative is if:
- the thing itself might change, and so the determination of its definition might change along with it
- the determination of its definition might change, and so its previous definition might have been invalidated
If neither of those things are true, all of the conditions expressed in definition are declarative.
> I think it stems from the fact that you are only looking at expressions, forgetting that these are just part of, possibly implicit statements.
Pretty close. I’m only looking at expressions because that’s how you can achieve anything declarative. To the point that there are statements (implicit or otherwise), they can’t be declarative whether you have conditions or otherwise. Because…
> Basically, there is always an assignment.
If you’re restricted to only expressions, this doesn’t matter, because assignment is inherently definitional and idempotent. Assignment with that constraint is logically the same as equality assertion. In fact, some implementations of this concept will literally error if you try to change an assignment. As in:
a = b // okay, a is equal to b
a = b // yep, this fact has been established, carry on
a = c // if c isn’t equal, this will fail and nothing depending on a can proceed
> That means that you are effectively writing how something is being built instead of simply describing what it is.
Now I don’t understand. How can you define things without constraining what they are? If you don’t have a semantic “not”, every definition you could come up with is inherently the infinite set, even if you define all of the affirmative facts. Without some mechanism to distinguish Thing A from Thing B, you have effectively arrived at two definitions of Things.
You can say that declarative style is only constant assignment. It doesn't make sense to use = though because (unlike imperative style) assignment is all there is, in a way
I think they are always an artifact of imperative programming bleeding into a declarative framework.
In a fully declarative fashion, you would just have:
c where c is imperatively defined as being a or b depending on a condition/state.
A declarative if statement doesn't make sense.