Declarative programming is a paradigm that expresses the desired result, not how to achieve it. It uses rules and constraints to describe a user interface, the layout of a network, the structure of a document, or a language parser. Declarative languages are highly domain specific, offering a concise syntax for those applications.
Why
Declarative programming eliminates swathes of redundant programming and manual work. It's a primary form of automation in the computing world. It has some of the easiest source code to read and of all paradigms has the lowest level of abstraction. Programmers and users, in general, can quickly learn and be productive in declarative languages.
Given it's structured and regular nature, declarative source cooperates well with tools. From visual editors to syntax checkers, to translators, and the final execution engines, tools are king in this paradigm.
This article focuses on the benefits and core qualities of declarative programming. As with all paradigms, there are limitations. I’ll have to look at those in a future article.
User interfaces
Documents and user interfaces are perhaps the quintessential declarative programs. We can create forms that look something ilke this:
<Header>User Information</Header>
<P>Fill out this form, pretty please? We'll give you a cookie.</P>
<Grid ColumnCount="2">
<Text Value="Name"/>
<TextInput Name="name"/>
<Text Value="Age"/>
<Counter Name="age"/>
<Text Value="Country"/>
<DropDown Options="countries" Name="country"/>
<WhileValid>
<Button Label="Save" Clicked="onSave"/>
</WhileValid>
</Grid>
This fragment describes the structure and layout of the form. Our UI engine will use this to produce an actual graphical interface for the user. In the declarative paradigm, we aren't interested in exactly how that happens, only that this form will be presented to the user.
The WhileValid
tag shows that declarative languages need not be static. This interface will only display a "save" button if the inputs are valid. This dynamic nature is often considered the aspect that separates a user interface from a document.
A
Clicked
handler shows the event paradigm can be used to connect this interface to other code. As declarative languages are usually quite strict, and limited, they typically provide ways to hook in other paradigms.
Deployment configuration
Manually installing and configuring software can be burdensome and error prone. Declarative control systems instead offer a way to describe what should be installed and let the deployment engine take care of the rest.
host:
name: company_web
instances: 2
daemons:
- { service: web_serve_pro, version: 3.5 }
- { service: sshd }
ports:
- { port: 443, access: public }
- { port: 8080, access: vpn }
files:
- { directory: /web/, source: file_serv:/source/company_web/build }
Like the UI, we are declaring what we want the result to be. We don't care how the configuration engine gets us to that point. Some systems may even support live changes to the structure: a change to port
here will update the value on all the machines in the network.
Perhaps more apparent than the UI example, here we see that declarative languages expose a series of rules and constraints. It's the job of the declarative engine to produce a compliant system.
Parsers
Declarative languages can be used to interpret documents instead of creating them. A regular expression is a simple declarative program: it specifies what a string looks like and the regex engine can deconstruct the parts. For full languages we often see parser generators used:
blog_markdown = { elements } ;
elements = title | paragraph | empty ;
title = '#' clickbait | sensible ;
clickbait = power_word ' ' noun ' ' emotional_verb ' ' uncommon_word ;
sensible = error("is this code path ever used?") ;
empty = r?^$? ;
We can see a series of rules of constraints in this example. We're writing a language that describes the document we wish to parse. Again, we don't care how the engine gets us the parsed document, only that if it matches these rules it works.
Syntax and engines
Each of these examples uses a different visual syntax, but they aren't that different at the abstract level. These are all trees of rules, constraints, and conditions. Any one of them could be converted to a syntax that looks more like the other. The syntax is chosen to be the most expressive and least redundant for the target domain. A mismatch can lead to a language that is too bulky, or inconvenient to use.
One of the commonalities between all declarative languages is the engine. Declarative programs are rarely compiled directly to an executable form. Some are not compiled at all; the declarations are read directly and executed by an engine. Unlike a universal abstract machine, these engines are usually single purpose; they support just one domain specific language.
It's not programming
Declarative languages are often not considered programming languages. The domain specific languages often lack a lot of the constructs that one associates with traditional programming, such as flow control or memory management. People that specialize in these languages are often not referred to as programmers. This view should perhaps be taken as a testament as how much a language can simplify its domain.
Regardless of our definition, the declarative paradigm plays a significant role in programming. We can't ignore that it is used everywhere in a variety of forms. Even if one doesn't consider it real programming, they'd be a poor programmer if they don't use it. Domain specific languages are robust tools for reducing complexity and improving productivity on a wide variety of projects.
Top comments (0)