↓ Skip to main content

Why you should switch to declarative programming

·7 mins

We are reach­ing lim­its of what is feas­ible with im­per­at­ive lan­guages and we should move to de­clar­at­ive lan­guages.

When ap­plic­a­tions writ­ten in im­per­at­ive lan­guages grow, the code be­comes con­vo­luted. Why? Im­per­at­ively pro­grammed ap­plic­a­tions con­tain state­ments such as if X do Y else do Z. As Y and Z con­tain in­vis­ible side-effects the cor­rect­ness of the pro­gram re­lies on some im­pli­cit in­vari­ant. This in­vari­ant has to be main­tained by the pro­gram­mer or else the code will break. Thus each time a new fea­ture is ad­ded to an ap­plic­a­tion or a bug is fixed the code for the ap­plic­a­tion gets more com­plex as keep­ing the in­vari­ant in­tact be­comes harder. After a while the code be­comes spaghetti-code and bugs are in­tro­duced as the pro­gram­mer fails to main­tain the in­vari­ant. This is go­ing to hap­pen des­pite the best in­ten­tions of the pro­gram­mer to keep things clean. Why is this?

An im­per­at­ive lan­guage is a type of lan­guage that tells the com­puter what to do and in which or­der. How­ever, most, if not all, ap­plic­a­tions are noth­ing but a trans­la­tion of some busi­ness do­main into a com­puter pro­gram. In or­der to get the im­per­at­ive code the pro­gram­mer has to trans­late the busi­ness model to a set of im­per­at­ive in­struc­tions, the busi­ness lo­gic. The im­per­at­ive in­struc­tions bear little re­semb­lance to the ori­ginal de­scrip­tion of the busi­ness model. When the busi­ness model changes the im­per­at­ive coun­ter­part could change en­tirely but what hap­pens is that pro­gram­mers make in­cre­mental up­dates to the code. This is done be­cause either they do not see that a more drastic change is ne­ces­sary or be­cause they are un­der pres­sure to de­liver res­ults. Over time this leads to bugs and un­main­tain­able code. Sum­mar­ised, bugs are in­tro­duced be­cause there is a manual trans­la­tion step between the busi­ness model and the pro­gram code.

Ima­gine a sys­tem for cal­cu­lat­ing whether a per­son should re­ceive a cer­tain al­low­ance. To re­ceive the al­low­ance a per­son has to meet sev­eral cri­teria such as age > 18 and income < 2400. We can de­note this in the fol­low­ing way:

def receives_allowance?(age, income)
  age > 18 && income < 2400
end

There are sev­eral re­marks to be made for this code. Adding a cri­teria such as mar­ital status would in­volve adding a para­meter to the func­tion and chan­ging the boolean ex­pres­sion. We could already avoid hav­ing to change para­met­ers when we had chosen a para­meter of type Per­son that con­tains the in­form­a­tion about a per­son. But what if we would in­tro­duce time as an as­pect in our cri­teria? We need to change the func­tion again. And what if the age cri­teria changed? If the pro­gram­mer er­ro­neously codes some­thing like age > 18 && age < 18 into the con­di­tion we would only find the bug dur­ing test­ing, if we are lucky. Ad­di­tion­ally when our cri­teria be­come more com­plex we would like to ex­tract cri­teria to their own func­tions. In short, it is easy to make mis­takes this way.

A solu­tion to this is to avoid the trans­la­tion pro­cess by us­ing a de­clar­at­ive lan­guage. A de­clar­at­ive lan­guage is a lan­guage that de­scribes what is to be com­puted but not how it should be com­puted.1 In es­sence: it omits control-flow. By en­cod­ing and thereby re­cord­ing the busi­ness model into a set of de­clar­at­ive state­ments it be­comes easier to spot ir­reg­u­lar­it­ies in the busi­ness lo­gic as the busi­ness lo­gic reads more like the de­scrip­tion of the busi­ness model and all in­vari­ants are vis­ible. In this man­ner the pro­gram­mer no longer tells the com­puter how to per­form a com­pu­ta­tion but rather what the com­pu­ta­tion should be. This makes it easier to main­tain

How­ever, we take it even to an higher level en­tirely. By just us­ing the de­clar­at­ive lan­guage, such as Haskell or Pro­log, you are still us­ing a general-purpose lan­guage and are thus lack­ing do­main spe­cific checks. It would be ad­vant­age­ous to de­vise a Do­main Spe­cific Lan­guage (DSL) in­stead. This would be a lan­guage spe­cific­ally geared to­wards your busi­ness do­main and can be done eas­ily in a lan­guage such as Haskell. Cre­at­ing a DSL has a great be­ne­fit: Be­cause the busi­ness do­main is writ­ten down in code the re­spons­ib­il­ity of trans­lat­ing the busi­ness do­main into a pro­gram shifts from the pro­gram­mer to the in­ter­preter of the DSL. This has two be­ne­fits: the trans­la­tion is con­sol­id­ated in one single point (the in­ter­preter) and can be veri­fied or even proven to be cor­rect. It could be checked that no con­tra­dict­ing state­ments are present. In this sense we can com­pare the val­id­a­tion to a spell-checker or JSLint but for our spe­cific do­main. Secondly the pro­gram­mer can­not make mis­takes in the trans­la­tion of the busi­ness lo­gic to im­per­at­ive code.

A simple DSL em­bed­ding the idea of the al­low­ance could look like the text in the ex­amples be­low. The in­ter­preter / com­piler is able to in­spect the sep­ar­ate rules and check whether they would cause a tau­to­logy or con­tra­dic­tion.

We will il­lus­trate this with some ex­amples. Con­sider the fol­low­ing text be­low, it is easy to read and it is clear what it means. Each line is a con­di­tion that has to hold, so we could read an “AND” at eacht line end. (Whether In­come is monthly or an­nu­ally or weekly is not taken into ac­count here but you get the pic­ture.)

A person is an adult when his Age is GREATER THAN OR EQUAL TO 18
A person is allegeable for an allowance IF
  he is an adult
A person is allegeable for an allowance IF
  his Income is NOT GREATER THAN €2400

When we would add a rule as shown be­low, we could get a warn­ing or er­ror from the in­ter­preter telling us that we have two dif­fer­ent con­di­tions on a person’s age.

A person is an adult when his Age is GREATER THAN OR EQUAL TO 18
A person is allegeable for an allowance IF
  he is an adult
A person is allegeable for an allowance IF
  his Income is NOT GREATER THAN €2400
A person is allegeable for an allowance IF
  his Age is GREATER THAN 21

And when we would add a rule as this it could warn us that no-one will ever get an al­low­ance.

A person is an adult when his Age is GREATER THAN OR EQUAL TO 18
A person is allegeable for an allowance IF
  he is an adult
A person is allegeable for an allowance IF
  his Income is NOT GREATER THAN €2400
A person is allegeable for an allowance IF
  his Age is LESS THAN 12

Not only would the in­ter­preter be able to spot these kinds of er­rors but it would also be easier for the writer of these rules to spot whether there is a mis­take. The as­tute reader will have no­ticed than we have not in­cluded any con­trol flow into our lan­guage mak­ing it a de­clar­at­ive lan­guage.

Be­cause the business-logic is now rep­res­en­ted by the DSL it can be writ­ten by do­main ex­perts in­stead of the pro­gram­mers of the ap­plic­a­tion it­self. The com­piler can provide feed­back when some­thing is wrong in the DSL and there is less chance for er­rors in the im­ple­ment­a­tion of the busi­ness lo­gic. Ad­di­tion­ally this frees the pro­grammes for im­ple­ment­ing bet­ter trans­la­tions of the DSL or other pro­jects sav­ing time and other re­sources.

To sum­mar­ise 4 reas­ons why you should switch to de­clar­at­ive lan­guages:

  1. Dir­ect trans­la­tion of the busi­ness model into busi­ness lo­gic
  2. Bet­ter read­ab­il­ity of the busi­ness lo­gic
  3. Bet­ter scalab­il­ity for the pro­gram in terms of func­tion­al­ity
  4. Less bugs

And 3 reas­ons why you should use DSLs to boot:

  1. Free up pro­gram­mers to do im­port­ant stuff
  2. Let the domain-experts handle the busi­ness lo­gic and have it machine-checked!
  3. It is just awe­some