What, If Anything, Is A Declarative Language?

Back in the 1980’s it was very fashionable to talk about “declarative” programming languages. But to my mind there was never a clear definition of a “declarative language”, and hence no way to tell what is declarative and what is not. Lacking any clear meaning, the term came to refer to the arbitrary conflation of functional with logic programming to such an extent that “functional-and-logic-programming” almost became a Germanic thing-in-itself (ding an sich). Later, as the logic programming wave subsided, the term “declarative”, like “object-oriented”, came to be an expression of approval, and then, mercifully, died out.

Or so I had thought. Earlier this week I attended a thriller of an NSF-sponsored workshop on high-level programming models for parallelism, where I was surprised by the declarative zombie once again coming to eat our brains. This got me to thinking, again, about whether the term has any useful meaning. For what it’s worth, and perhaps to generate useful debate, here’re some things that I think people mean, and why I don’t think they mean very much.

“Declarative” means “high-level”. This just seems to replace one vague term by another.

“Declarative” means “not imperative”. But this flies in the face of reality. Functional languages embrace and encompass imperative programming as a special case, and even Prolog has imperative features, such as assert and retract, that have imperative meaning.

“Declarative” means “functional”. OK, but then we don’t really need another word.

“Declarative” means “what, not how”. But every language has an operational semantics that defines how to execute its programs, and you must be aware of that semantics to understand programs written in it. Haskell has a definite evaluation order, just as much as ML has a different one, and even Prolog execution is defined by a clear operational semantics that determines the clause order and that can be influenced by “cut”.

“Declarative” means “equational”. This does not distinguish anything, because there is a well-defined notion of equivalence for any programming language, namely observational equivalence. Different languages induce different equivalences, of course, but how does one say that one equivalence is “better” than another? At any rate, I know of no stress on equational properties of logic programs, so either logic programs are not “declarative” or “equational reasoning” is not their defining characteristic.

“Declarative” means “referentially transparent”. The misappropriation of Quine’s terminology only confuses matters. All I’ve been able to make of this is that “referentially transparent” means that beta-equivalence is valid. But beta equivalence is not a property of an arbitrary programming language, nor in any case is it clear why this equivalence is first among equals. In any case why you would decide a priori on what equivalences you want before you even know what it means to run a program?

“Declarative” means “has a denotation”. This gets closer to the point, I think, because we might well say that a declarative semantics is one that gives meaning to programs as some kind of mapping between some sort of spaces. In other words, it would be a synonym for “denotational semantics”. But every language has a denotational semantics (perhaps by interpretation into a Kripke structure to impose sequencing), so having one does not seem to distinguish a useful class of languages. Moreover, even in the case of purely functional programs, the usual denotational semantics (as continuous maps) is not fully abstract, and the fully abstract semantics (as games) is highly operational. Perhaps a language is declarative in proportion to being able to give it semantics in some “familiar” mathematical setting?

“Declarative” means “implicitly parallelizable“. This was certainly the sense intended at the NSF meeting, but no two “declarative” languages seemed to have much in common. Charlie Garrod proposes just “implicit”, which is pretty much synonymous with “high level”, and may be the most precise sense there is to the term.

No doubt this list is not exhaustive, but I think it covers many of the most common interpretations. It seems to me that none of them have a clear meaning or distinguish a well-defined class of languages. Which leads me to ask, is there any such thing as a declarative programming language?

[Thanks to the late Steve Gould for inspiring the title of this post.]

[Update: wordsmithing.]

[Update: add a remark about semantics, add another candidate meaning.]

[Update: added link to previous post.]

Share this:

Like this:

LikeLoading...

Related

This entry was posted on Thursday, July 18th, 2013 at 1:34 pm and is filed under Research, Teaching. You can follow any responses to this entry through the RSS 2.0 feed.
You can skip to the end and leave a response. Pinging is currently not allowed.

Post navigation

24 Responses to What, If Anything, Is A Declarative Language?

Definition #4 is definitely the best, the others just describe particular properties of a declarative language.

Operational semantics is NOT necessary to define such languages; it only harms because forces the programmer to visualize how a program is executed and sort of ‘think like a computer’. Operational semantics can be useful for an implementer, but it can be defined rather arbitrarily, as soon as it matches the denotational semantics.

I can give at least one example of a declarative programming language, if anyone is interested.

I’d wager the parallelism folks were using it as an approximation of “not effectful”, since even though FP and LP can have imperative constructs and effects, usually they steer towards fragments or programming styles that don’t use them, right?

Maybe I’m the only one, but I find “declarative vs. imperative” useful as a loose analogy with natural language. It only goes so far, but there is a squishy “feels-like” sense in which writing in ML and Prolog involves more stating-of-relationships and programming in C involves more issuing-of-commands. I do think this is meaningfully different from high-level/low-level, but I’m only so far able to convey my intuitions, not (whether there is) anything more precise to say.

This seems like a pretty useful definition to me, and I think satisfies most intuitions regarding the nature of declarative expressions. Naturally, this excludes traditional functional programming (although I think Haskell can support these qualities).

Technically, your analysis seems sounds: that the people working on declarative languages for parallel programming haven’t provided a clear technical demarcation between “declarative” and “imperative” languages. I think that rather misses the point though.

From the point of view of someone who actually has a parallel programming problem to deal with, in different languages tend to support different styles of programming when it comes to organizing data, managing synchronization, and so on. Depending on the language and libraries I use, there do seem to be distinct styles that require thinking about concurrency and parallelism in very different ways.

In particular, it’s a unique experience working on programs that require careful reasoning about the interleaving of instruction streams of different threads/processes, especially when shared mutable state is involved. I think it’s totally fair to describe this as “imperative”, or to say that it emphasizes the “how” rather than the “what”, even if theoretically it is not a clearly demarcated class of programs.

I think your point is at least in part the same as that of my post Parallelism Is Not Concurrency. One might say that parallelism is “declarative” and that concurrency is “imperative”, but I am still skeptical. In my view parallelism is about performance, concurrency is about composition. I don’t see that this is really the same distinction.

I would say that a noble goal is to allow code to be written more in terms of its problem domain and less in terms of its machine model. An early attempt to define this was to be “higher-level”, which roughly means “performing more computation per symbol, on average”.
The problem with the term “higher-level” is that the goal of domain-specific code is implicit; we presume that, given the extra expressive power, it will be used to more effectively model solutions to our problems. In reality, we ended up with incredibly abstract code, but which is still tied to machine models. Those machines are now virtual, and as such they can implement much nicer models than we’re stuck with on physical machines, but they’re still generic and universal, so we’ve got no closer to solving any particular problem.
Since the meaning of “higher-level” has been diluted down to its common denominator of “abstract”, along comes “declarative” to re-focus the attention on the goal.
I would say a reasonable definition of code’s ‘declarativeness’ is the extent to which it reduces the amount of subsequent code required to solve any of the program’s goals.
We can use this to make a scale:
* Machine code/assembly is not very declarative, since any particular operation is one small step on a long, winding path towards a goal.
* (Higher-level) imperative code is usually more declarative than machine code, since we can take longer strides; however, by heading towards one goal we get further from the others.
* Functional code is usually more declarative than imperative code, since we tend to spend our effort on building a motorbike which we can then use to rapidly approach any of our goals.
* Logic code can be more or less declarative than functional code: we take the built-in taxi to where we want to go; whether it’s better than our functional motorbike depends on how many directions we need to give to the driver.

I think the main purpose of “declarative” is to rhyme with “imperative” in the as-yet-unwritten PL version of “I am the very model of a modern major general”.

Kidding aside, I think the only sensible meaning of “declarative” is “high-level, what/not how”, e.g. for describing domain-specific “description” languages. Mandelbaum et al.’s PADS language comes to mind. But as you point out, the broadening to include functional and logic programming is clearly senseless.

While we’re at it, and forgive me if you’ve already posted on this, what do you think defines a “functional” language? To me, that term is rapidly becoming near-meaningless as well.

I agree with your commenters, and Hassan in particular. To elaborate on his point, what I think is missing (but is alluded to) is explicit consideration of the developer’s intent.

While imperative languages are technically declarative, they require you to declare more than you want to. For example, they require you to specify an order for execution even if you don’t care about that order. This is a corruption of the intended semantics of your expression.

In other words, the more declarative a language is, the more easily a developer can declare his intentions without being forced by the language to include extraneous constraints.

I’m betting that “declarative”, like “object-oriented”, actually names a style of program-writing, more than a class of languages; but even from that point of view on the adjective, it’s surely possible to design a language (or maybe a virtual machine interpreting a language) that encourages one style over another. I remember learning of the “imperative” constructs in scheme some years ago and thinking the whole thing felt horridly un-schemely. Still haven’t got my head around “thunks” and such.

And I don’t think one should ignore the influence of presentation style on use, even among equivalent things. To take an example of equivalent things, Delta-sets present more-or-less the same things as CW complexes, and yet: it’s frustratingly easy to prove/construct some functor on the category of Delta-things vs. CW-complexes (they almost invariably run by just applying the obvious functor on sets at each grade) but I’ve seen precious few specific Delta-sets that I could actually work with. Presentation matters.

Now, whether declarative style is *good* style… ask someone else. And whether one should design the presentation of a type system so as to encourage (or ease) a declarative style… your guess is better than mine.

What puzzles me is that people seem to use the term to distinguish a class of languages, yet I can’t really find anything in common amongst the “preferred” languages. Everyone seems to have their own idea of what is “declarative”, but there doesn’t seem to be any clear commonality other than the speaker’s approval.

There are many real-world domains where giving users the ability to describe the “what” rather than the “how” is very useful, leading to systems that are easier to reason about by the users, and by the implementers. My community uses “declarative” to describes these systems. The argument that the existence of an underlying operational semantics means that declarative and imperative are equivalent is a specious, at least in my practical experience.

All your points well taken, Bob – but IMHO a tad on the extreme in all the dimensions you cite. Shouldn’t “declarative programming” be understood more as a relative notion in any of these dimensions rather than an absolute one? Granted that, say, Prolog has imperative constructs (assert/retract, order of clauses, etc., …). But the effort of specification of a working program on a programmer for a given application is in general much less strenuous in Prolog than it is in C or Java – at least in my experience. In this regard, being (relatively more) declarative for a programming idiom means allowing (relatively more) abstract specifications for programs to run. By “more abstract” one understands having to spell out a lesser amount of details. This may be what your first item (“high level”) that dismiss as “vague” alludes to. So be it. But this “vagueness” has spared me (and many others I believe) a great amount of pain and time.

So it seems that, as the French say, you are “throwing away the baby with the bath water”…