4
$\begingroup$

I had been first introduced to the idea of declarative syntax when using Angular JS. From what I understand, the idea is that you say, "do this" instead of "in order to do this, do this, this and this". You don't care how it's done, you just want it done, and you pass it off to some lower level of abstraction to do.

Now I'm going over the idea of encapsulation as I learn Java, and the idea seems very similar. From what I understand, the idea is that you break things up into modules, and you define an outwards-facing API for people to use. So people could use your in a declarative manner to say, "do this".

Is this true? If so, what is the real difference between declarative syntax and encapsulation? Just that one describes syntax and the other describes the more abstract design philosophy?

Edit: I think that my question boils down to: what's the difference between making a declarative statement and making an API call?

$\endgroup$
4
$\begingroup$

They're orthogonal.

In declarative programming, you describe what would count as an acceptable solution, without necessarily describing how to find it. For instance, a declarative program might have rules like "if you want to install the package gcc, you must have first installed the binutils and cpp packages" and "if you want to install the binutils package, you must have first installed coreutils". It's up to the platform to figure out a way to meet all of these constraints.

Encapsulation refers to protections that prevent the client of an API from tampering with the internal state of the service that implements the API. Nothing here says the API has to be declarative in nature. You could have a API that is more declarative in nature (e.g., the client provides some constraints, and the service figures out how to meet them), or you could have an API that is more operational in nature (e.g., the client specifies what actions should be performed, rather than listing the desired goal and leaving it to the service to figure out what actions will accomplish that goal).

So, you can have an encapsulated API that is not declarative in nature.


To elaborate: invoking an API is not necessarily declarative in nature. You can design APIs that are declarative in feel; and you can also APIs that are operational in feel. Not all APIs will be declarative. For example:

  • A declarative API might let you give it some rules/constraints, and then specify the goal (I want gcc to be installed), and then let the service infer a sequence of actions that will achieve your goal (a sequence of packages to install, and what order to install them in). For example, the client might call findWayToInstall("gcc"), and the service comes up with a sequence of packages to install that ends with gcc installed -- that'd be a declarative API.

  • An operational API might let you repeatedly give it an action to perform (e.g., you repeatedly invoke installPackage(); this succeeds if you've met all the prereqs, and it's up to you to identify a valid sequence of packages that ends with gcc installed). For example, the client might call installPackage("coreutils") then installPackage("binutils") then installPackage("cpp") then installPackage("gcc"), and if this sequence of operations meets all of the prerequisites, it ends with gcc installed. With this API it is the caller's responsibility to identify a sequence of actions that ends with gcc installed. Thus, this kind of API is more operational than declarative in nature.

Of course, declarative vs operational are just concepts to help us talk about properties of programming languages and APIs. There's no hard, bright line between them. It's not black-and-white; there's a spectrum. But they are useful concepts nonetheless.

$\endgroup$
  • $\begingroup$ Nothing here says the API has to be declarative in nature. What I was thinking is that the act of using an API is declarative. It's saying, "Hey API, I want this done, so go get it done". Is that true? You mention that encapsulation involves preventing the client from messing with the internals, but that seems to be the case when you make a declarative statement also. I guess my point boils down to, "making a declarative statement seems like the same thing as making an API call". $\endgroup$ – Adam Zerner Mar 27 '15 at 22:58
  • 1
    $\begingroup$ @AdamZerner, see my updated answer -- the second half tries to address your comment more explicitly. No, not all APIs are declarative. Using an API is not inherently declarative -- you can design APIs that are more operational in flavor than declarative. $\endgroup$ – D.W. Mar 27 '15 at 23:12
  • $\begingroup$ Ahh ok, that makes sense now. Thank you very much! $\endgroup$ – Adam Zerner Mar 27 '15 at 23:33
  • $\begingroup$ I ended taking a stand for the OP initial view. I am seriously bothered that we cannot use better words than feel, style, flavor, to characterize declaraitveness. I wanted to see whether I could get more by relying on something more formally defined, but it actually got worse, as it seems to vindicate the OP's view. I do not mean that view is bad, but it does not help with the style/feel/.flavor aspect of declarativeness. cc @AdamZerner $\endgroup$ – babou Mar 28 '15 at 2:26
1
$\begingroup$

In a nutshell

My first impulse was to agree with D.W.'s answer that the two concepts are orthogonal. On second thought, I think it is only partially true, and I will also try to argue that they are two sides of the same coin.

In first approximation, declarative programming just specifies what you want and lets the system find how to get it, while imperative programming gives precise instructions to the system that ensure you get the result you want. In practice, you usually have a mix of both. You may add hints to your declarative programming so that the system can better find how to do it. Conversely imperative programming often includes high level concepts that let you skip over some details that are automatically handled by the system.

Since encapsulation helps you increase the number of high level concepts that you can use just as needed, it is quite fair to consider that encapsulation also supports a more declarative form of programming.

To put it another way:

Declarative programming assumes that the compiler/interpreter will include something like a theorem prover or a solver that will find how to do it from a specification of what is to be done.

Encapsulation is a way, for some predefined cases, to have the proofs already prepared and applied as code in the encapsulation. The specification of what is to be done hiding the internal description of how to do it.

Denotational semantics does not make a difference between $3+4$ and $7$. Should we then consider that there is much difference between a proof and code to be done and a canned proof and code already done?

In that respect, one can consider, as does the author of the question, that encapsulation has strong similarities to declarative programming.

But this is also to a large extend a matter of style, and of interpreting what is written.

BTW: I do not like very much the expression "declarative syntax". The issue is very much a semantics issue. "Declarative style" would be a more appropriate expression.

Declarative programming

To better explain my understanding of declarative programming, I will try to use, as much as I understand it and somewhat loosely, the Curry-Howard isomorphism. One basic idea of this isomorphism is that it describes a correspondence between programs and proofs, and between program specifications and theorems.

So a program may be read as a (constructive) proof of its specification. Conversely, given a mathematical statement with some existentially quantified variables, you can turn it into a theorem by providing a proof, and if you limit yourself to constructive proofs, you can extract from that proof a program that actually tells you how to make the statement true.

For example if the statement is $\forall a,b,c \in \mathbb C,\; \exists x\in\mathbb C,\; ax^2+bx+c=0$, you can extract from the proof an operational way of solving second degree equations. In some cases, the proof may work only under some conditions, which will be conditions you impose on the data for the program to run.

BTW The word algorithm comes from the name (of the home town) of a Persian mathematician who was solving equations. It is all consistent.

Some research on automated program generation consists precisely in taking the specification of a program as a theorem to be proved, in some appropriate formal theory. A proof assistant will record all the formal steps of the proof, which can be partially automated, and then the program meeting the specification is extracted from that proof.

Ideally (sometime in the future in most cases), if the whole process is fully mechanized with a theorem prover and does not need human intervention, you can just specify your program, as use the system to compile the specification into running code.

This approach is the ultimate declarative programming: you describe what you want, but not how to get it. An you can leave it to some kind of deductive system the task of determining the steps to achieve the desired results.

This is used in practice, not so much to mechanically generate complex programs, but to handle automatically simple problems without requiring the programmer to specify all the operational steps. To take a trivial example, the steps for bootstrapping a compiler written in its owns language with an interpreter can be found automatically by a small logic program. And computer science has many other examples, in very specialized area where it is tedious, but easy, to determine the steps needed to achieve a result.

Encapsulation

The principle of encapsulation is to take some meaningful subpart of a program, and represent it by an interface which actually specifies it, so as to hide the way it is actually implemented. The specification includes precise interfaces (API) to use the encapsulated code, but may also include other information, assertion, or formal mathematical statement about the API, that fully specifies what it is supposed to do, and possibily some pragmatic properties regarding performance, or even some assertions about complexity. Of course, such full specification are not so common.

This has several purposes. It gives a precise specification of part of a program, abstracting away the implementation details so as to simplify reasonning by considering only high level concepts and properties defined in the interface. It allows easy reuse of the encapsulated code in other program. It allows making improvements to the encapsulated program without affecting the larger programs that use it, as long as the specification is respected by all. And more ... but let's stay on topic.

Once you have encapsulated some concepts, you can use the encapsulation in any program so as to have the concepts available for programming without having to tell more about the details of their implementation. Note, in passing, that the high level language compiler also provides that kind of service.

If you see your program as a proof, it becomes a simpler proof, with less details, thus closer to the specification it proves. So in this sense, the encapsulation helps you to be more declarative. You use only its specification, and forget the implementation which is only a canned proof of that specification (so you can forget the theorem prover).

Now, the API provided by an encapsulation may be seen as more or less declarative, as explained by D.W. depending on whether the primitive operations it provides seem to be more specification of what should be done, or low level operational steps.

The distinction is important, as trying to be more declarative may have a impact on the properties of your program (readability for example). But it is also very much in the eyes of the beholder.

By nature, an encapsulation will be somewhat more abstract, higher level, that what it is implemented with. In that sense it is necessarily more declarative.

Another point is that asking for an equation to be solved is a very declarative kind of thing to do. But how different is the call solve2(a,b,c) to solve a second degree equation from the call matrix3(a,b,c) to make a 3-dimensional matrix, which may be seen as very operational.

Programmers tend to consider that thing are more declarative when using logical rules, constraints, equation, that have to be applied or resolved, rather than computation steps to be executed.

But even these distinctions are not always clear cut.

My favorite example is context-free grammars. Most people learn to see them as rewriting systems, which are a rather operational kind of object. All the words are generated by rewriting other words. But a context-free grammar may equally be seen as a set of language equations, which is a very declarative kind of thing.

Note that this answer in not in contradiction with D.W.'s answer. But I do believe that your initial concern was well justified. There is a matter of style, of feel (says D.W). But there is also a matter of level of discourse. The higher the level, the less you need to prove, and the more declarative you are. And encapsulation helps that, by providing high level definitions. That was my reason for bringing in the Curry-Howard isomorphism and automated programming.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.