A Hundred Year Language

| 0 Comments | 0 TrackBacks
The right way . . . is to separate the meaning of a program from the implementation details.Saying less about implementation should also make programs more flexible. Specifications change while a program is being written, and this is not only inevitable, but desirable.

Traditional programming languages divide a program into two parts: comments and code. The comments describe the goal of the code while the code describes how those goals are met. Bugs arise when the two don't agree. Most programmers would rather do one or the other, but not both. Doing both seems a waste of time because too much information is being duplicated in a way that can introduce mistakes. One of the primary goals of the Gestinanna project was to collapse the comments and code into a single descriptive document that would both describe the program and be the program.

Literate programming tries to solve some of these problems, but it still results in the programmer having to supply the procedural solution as well as describing the problem instead of simply describing the goal of the solution.

What we need is a method of writing a program (the goal of the solution) that is both human and computer readable. The most widely understood form that meets this requirement is XML. By writing a program using an XML vocabulary, we create a document that can easily be transformed into something readable by people (e.g., HTML) and something that can easily be compiled into a procedure that acomplishes the goal (e.g., a Perl module). The resulting XML vocabulary will need to borrow heavily from functional programming in the way it approaches the problem of describing solutions. Functional programming thinks in terms of data transformation instead of sequential instructions. All computing is simply transforming data. Examples of functional languages include LISP, Haskell, and XSLT.

All applications basically take input data, apply some transformations, and provide output data. Some are fancier in how they do this, but everything can be broken down into that basic form. Desktop applications, web applications, servers. It doesn't matter what role the application plays, it still takes input, transforms it in some way, and provides output. Some applications even change the transformations based on past input. All applications are state machines.

Since all applications are state machines, we can define the root XML element for our programs: <state-machine/>. Of course, state machines have states. Our next element is then <state/>. We'll want to give it an identifier so we can refer to it, so we will add an id attribute. We should be able to add an id to a <state-machine/> as well so we can reference it from other <state-machine/>s.We want to be able to add data transformations to the state machine. Otherwise, the state machine doesn't really do anything. We can do this with a <transform/> element. Sometimes, we need to apply a transformation to an empty value to initialize a program. We need to specify when certain transformations should happen, so we will add the <init/> and <clean-up/> elements to act as special <transform/>s.

This will initialize the state machine with a list of work flows based on the /example/requests work flow definition that are in the PENDING, APPROVED, or DENIED state and owned by the person logged in to the website. It will transform the list of work flows into a list of associations, one association for each work flow. Each association will have four key/value pairs: the id of the work flow, its state, the name of the requested list (e.g., an e-mail list), and the date the work flow was last updated. This list will be available as /requests in any subsequent processing (e.g., a Template Toolkit view might access the list as requests[]).

In an implementation, the contents of the <init/> can be lazily evaluated. For example, in Perl, the <init/> can be stored as an anonymous subroutine that is called when the value of /requests is actually needed. Because of this, we can think of the <init/> as being applied when we use a value that matches /requests. This leads to an expectation that arbitrary XPath-like expressions can be matched against. The <init/> thus provides a default value for any data element that matches the match attribute.

Continuing this line of thinking, the <init/> and similar container elements don't really contain scripts that are run, but sets of rules that are applied to various bits of data at the appropriate time. The <init/> and similar elements act as scopes for the data transformation rules. If we are applying data transformation rules instead of steps in a script, then we can make use of the parallelism that functional programming allows. The rules are independent of each other and have no side effects. We can compress the rules by looking for common phrases and computing them once when needed.

Next time, we'll look at how states in the state machine can be specified and how flow through the state machine can be managed.

No TrackBacks

TrackBack URL: http://www.jamesgottlieb.com/cgi-bin/mt-tb.cgi/4

Leave a comment

Recent Entries

Fabulator Design: RDF Operations
If you’re familiar with MVC design, or at least with Ruby on Rails, then you’ve heard of CRUD: create, read,…
Fabulator: the Future of Gestinanna
In a post from quite a while back, I talk about eXtensible State Machines, a way to reduce a web…
The Church of Latter Day Scholars
Michael Godwin, General Counsel, Wikipedia Foundation, is on campus today visiting with various digital humanities groups and giving a talk…