[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]
Declarative programming requires a different mindset
- From: "Costello, Roger L." <costello@mitre.org>
- To: "xml-dev@lists.xml.org" <xml-dev@lists.xml.org>
- Date: Sun, 14 Mar 2010 15:24:12 -0400
Hi Folks,
I learned imperative programming in school. Learning to write programs declaratively has been a slow process for me.
I'd like to share with you some exciting, fundamental insights that I've had recently. Thanks to Mary Holstege, Markus Pilman, and Michael Kay for expanding my mind. I invite you to describe your insights into declarative programming.
First, distinguish between imperative and declarative programming:
Imperative: specify how to do it; prescriptive.
Declarative: specify what you want; descriptive.
Imperative programming is like a recipe: do this first, then that second, and so forth. Statement A must be executed before statement B.
In declarative programming "before" and "after" are meaningless. Statements can be executed in any order, even in parallel.
Below I examine two examples that illustrate--in a rather dramatic fashion--the differences between imperative and declarative programming.
EXAMPLE #1
Consider this XML document that contains the name and favorite color of members of a fitness center:
<FitnessCenter>
<Member id="1">
<Name>Jeff</Name>
<FavoriteColor>lightgrey</FavoriteColor>
</Member>
<Member id="2">
<Name>David</Name>
<FavoriteColor>lightblue</FavoriteColor>
</Member>
<Member id="3">
<Name>Roger</Name>
<FavoriteColor>lightyellow</FavoriteColor>
</Member>
</FitnessCenter>
Note that each member has an id attribute.
I want to change the document so that each member is identified by his name. I want to change this:
<Member id="1">
<Name>Jeff</Name>
<FavoriteColor>lightgrey</FavoriteColor>
</Member>
to this:
<Member id="Jeff">
<FavoriteColor>lightgrey</FavoriteColor>
</Member>
Here's some pseudo code that shows how to accomplish this:
For each <Member> element:
1. replace @id with the value of <Name>
2. delete <Name>
Here are some key things to note:
A. In my preamble I said "... pseudo code that shows how to ..." That's prescriptive.
B. There is a definite order of actions -- delete <Name> only after extracting its value.
This is all very imperative. Is declarative programming not useful for this application?
In fact declarative programming is well suited for this application. Let's recast the pseudo-code into the declarative mindset:
For each <Member> element:
The new document has the id attribute's value replaced
with what was previously the value of the <Name> element, and
the <Name> element no longer exists.
Here is a key thing to note:
The new pseudo-code is a description of the new document.
It is not a recipe for what actions to take.
Thus, in declarative programming it would be perfectly fine to "delete <Name>" first!
For each <Member> element:
1. delete <Name>
2. replace @id with the value of <Name>
In an imperative mindset, this would be nonsensical -- you cannot delete <Name> and then later use it.
In a declarative mindset, it is perfectly fine - you are stating facts about the new document. Order is irrelevant.
EXAMPLE #2
Consider this new version of the fitness center, where some members have a record of their Body Mass Index (BMI):
<FitnessCenter>
<Member id="1">
<Name>Jeff</Name>
<FavoriteColor>lightgrey</FavoriteColor>
<BMI-Values>
<BMI date="2009-10-01">36</BMI>
<BMI date="2009-12-01">32</BMI>
</BMI-Values>
</Member>
<Member id="2">
<Name>David</Name>
<FavoriteColor>lightblue</FavoriteColor>
<BMI-Values>
<BMI date="2010-01-04">25</BMI>
</BMI-Values>
</Member>
<Member id="3">
<Name>Roger</Name>
<FavoriteColor>lightyellow</FavoriteColor>
</Member>
</FitnessCenter>
Note that each <BMI> element is nested within a <BMI-Values> element.
I want to insert this <BMI> element into the Roger member:
<BMI date='2010-01-01'>10</BMI>
The imperative approach would be to execute this sequence of actions:
1. If there is no <BMI-Values> element then add one.
2. Insert the new <BMI> element into the <BMI-Values> element.
That approach won't work for declarative programming. Express it declaratively and you can see why:
In the new document the <Member> will have a <BMI-Values>
element if it didn't already. The new <BMI> element
will be nested within the <BMI-Values> element.
Look at this <Member> element:
<Member level="platinum">
<Name>Roger</Name>
<FavoriteColor>lightyellow</FavoriteColor>
</Member>
How can we declare that in the new document the <BMI> element will be nested within the <BMI-Values> element, since there is no <BMI-Values> element? We can't.
The lesson learned in this second example is:
In declarative programming you cannot build
on top of previous actions.
The declarative approach to this example is:
The new document has nested within the <Member> element a
new <BMI-Values> element (and nested within it the new <BMI>
element), where the <Member> element previously had no
<BMI-Values> element. Where the <Member> element did previously
have a <BMI-Values> element, the new document has nested within
it the new <BMI> element.
Declarative programming rocks!
/Roger
P.S. XQuery and XQuery Update are declarative programming languages. Below I show how to express the above examples in the XQuery Update language.
EXAMPLE #1
let $source := doc('FitnessCenter.xml')
for $i in $source//Member return
(
delete node $i/Name,
replace node $i/@id with
attribute {'id'} {data($i/Name)}
)
EXAMPLE #2
declare namespace ex = "http://www.example.org";
declare variable $source := doc('FitnessCenter.xml');
declare updating function ex:insert-BMI ($person, $BMI)
{
if (empty($person/BMI-Values)) then
insert node <BMI-Values>{$BMI}</BMI-Values> as last into $person
else
insert node $BMI as last into $person/BMI-Values
};
ex:insert-BMI($source//Member[3], <BMI date='2010-01-01'>10</BMI>)
[Date Prev]
| [Thread Prev]
| [Thread Next]
| [Date Next]
--
[Date Index]
| [Thread Index]