Declarative Programming
From APIDesign
Contents |
Have You Ever Wondered...?
You have just scared us with runtime implications of APIs, is there a way to minimize their impact? Is there a way to prevent race conditions, deadlocks, etc.? Yes, as far as I know there are two ways. Either prevent shared access to data, objects, structures, resources or allow it. The chapter 12 explains how to do that with a little help of Declarative Programming. Declarative Programming!? Is there something like that? Well, that is indeed good question, which the chapter 12 analyses as well and it even suggest some good forms to use for this style of APIs including XML, PropertyFiles or other protocols.
Less is Better
The less flexible a declarative approach is, the better. The reason seems to lie in the fact that due to restricted expressiveness one can make more assumptions about the declarations and can optimize them, cache them, create general proxies that delay loading of the actual implementations, etc. If the declarative registration is as power as Turing Machine - e.g. as powerful as regular programming languages like C, Java, Perl, and all the others, then one cannot optimize much, and often needs to resort to actual execution of the registered code snippets.
This is caused by the presence of Halting Problem: there are programs that one cannot analyse to find out what they do, without executing them. And in such situations you can give up on any caching, or optimizations. If your program is written in Java and your declarations are in JavaScript you are unlikely to get much benefits - you need to interpret and execute all the JavaScript snippets as they can provide different results on Monday, or Tuesday or on first or second execution, etc. JavaScript is just too powerful language for declarations.
That is why, if one wishes to worship declarative programming inside existing imperative languages, it seems very important to make the declarative syntax less powerful than Turing Machine. Only then it can really bring some benefits and optimizations not available with regular execution of plain code.
Documenting Declarative APIs
Declarative Programming offers us higher level abstractions. Such abstractions usually allow API users to specify what shall be done without the need to describe how to do it. Nice difference between these two approaches can be seen when packaging an application in Maven or in Ant (the build.xml and pom.xml are APITypes). While in Ant I would need to copy all the library JARs into appropriate location and then generate main module manifest Class-Path attribute manually and ZIP the whole bundle, in Maven I can just choose the right plugin, tell it that my libraries shall are in lib directory and that I want Class-Path attribute and the rest is done automatically. This is perfect, but it has its own hidden issues.
It seems to me that the documentation for declarative APIs cannot be the same as for procedural ones. The higher level abstractions may be too abstract for consumption by regular API novice. As shown in my Maven adventures, I had a vision of what I wanted to do, but I could not select the appropriate abstractions (e.g. the Maven plugin) to help me in achieving my goal.
In case of procedural Ant, this is much easier. I know that I need to copy library JARs. Guess what is the appropriate tool? Yes, it is the copy task. I need to create a ZIP file. Guess what one shall use? Of course, the zip task. Finding the right mapping is easy, as Ant is naturally mapping the (simple) concepts user has in his head to the right tools. Of course, only simple concepts are mapped. As soon as one starts to think about generating the Class-Path attribute, one will get lost. This is just too complicated to be automated in Ant (as far as I am aware one of the right tools is pathconvert task, but the whole order of steps is quite complex).
Getting High
The trouble with high level concepts is that there needs to be some entry point that maps the actual API user need to the right tool. Treating the Maven plugins as such entry points is not sufficient. Every plugin has huge set of additional configuration options and can be tweaked to do almost everything. So when looking at assembly plugin documentation I have seen that this is useful for creation of uber JAR and I immediately have lost my interest. If I investigated it more, I would have found out it is useful for solving many other goals as well. But API shall be easy to use.
The entry point for an API should be a usecase. A description of an (expected) user need. This is true for any API, but clearly for Declarative Programming this is more important than ever. In this case the usecase is to package ZIP file with multiple JAR files. This would immediately trigger my (or google's, when I was querying for keywords like ZIP maven JAR Class-Path) attention and I could then follow up to the actual example describing how to realize my usecase via pom.xml configuration.
Abstractions Need Usecases
The more abstract concept you expose, the more important good usecase is. For example NetBeans is using Lookup concept, which is quite abstract and used for various, at first sight unrelated, goals. It is almost impossible to explain newcomers the Lookup API itself. It always needs to be introduced by real problem, real usecase. For example: if you want to MVC in NetBeans, you need Lookup. Use it this way!. Then the usecase is clear, and the actual (originally confusing) API is just a tool to realize that usecase.
Abstractions are soul of API design. They create the high level concepts that simplify our life (when one knows how to use it, the Maven is really more friendly than Ant). Yet, abstractions are not the primary goal. Humans need a guide to use the abstractions. Human need good usecases that document declarative APIs.
What Do API Users Say?
Too many developers are focused on the mechanics of how a task will be performed and the steps required to perform them. Typical documentation of software explains what command line switches are available, but not why you might want to use them. I have been frustrated by this for years. I always skip to the "Examples" section of any doc, as I usually can infer use cases from the examples far better than from studying method signatures in the abstract. A classic example would be the Java API docs. Why would I want to use a LinkedHashMap? Maybe I slept through that lecture in CS class. It would be neat to make a pivoted set of docs for the JDK in terms of patterns, like LRU Cache => LinkedHashMap. The only way I've learned Maven is to study other people's POMs, guess-and-check, and then discuss.
-- Basscakes 15:55, Sep 15, 2009