Many software and hardware producers take pride in the exponential
pace of technology change, but for users and consumers of their products
and services the rapid technological obsolescence often means increased
costs, frustrations, and unfulfilled promises. Corporate America
expects to make capital investments in goods and facilities that should
last five, ten, even twenty years, but only an eighteen-month lifetime
for computer software and hardware investment is not uncommon.
Lowering
the costs to develop new software solutions or extending the lifetime
of software applications are two complementary approaches to addressing
technological change. These goals can often be met by taking a
declarative strategy when designing software systems independent of the
programming methodology employed.
Issues with Imperative Programming
Most
programming projects today use the imperative style of programming.
Developers write sequences of operations in a language, such as C++,
Java, Visual Basic, etc., that implement an algorithm, or recipe, for
performing tasks. The algorithm for the task mixes logical, or
relational, statements about the task to be solved and control
statements about how to calculate the solution. The logical statements
describe "what-to" calculate while the control statements describe
"how-to" calculate. Debugging the algorithm consists of verifying the
accuracy of the logical statements and fixing the control statements, if
necessary.
There are many problems with the
imperative approach. The sequence of operations critically determines
the correctness of the algorithm. Unexpected execution sequences through
an algorithm caused by user input actions or real-time events in a
multitasking environment may result in subtle or catastrophic algorithm
failure. Writing the control logic is the programmer's responsibility
and, therefore, subject to implementation errors. Understanding a
program's algorithm is often difficult for other developers without
extensive metadata, or comments, on the code and empirical tracing of
the program's execution with sample data. Verifying program correctness
consumes a significant portion of the development effort, but also
usually fails to discover a significant number of defects.
To
address the problems associated with imperative programming, the
computer industry has developed and advocated many approaches.
Structured programming and campaigns against "go-to" statements address
some of the problems discovered with ad hoc control structures and
statements. Modularization initiatives stress decomposition techniques
on the premise that humans can better comprehend, reason about, and
maintain smaller pieces of code. Object-oriented programming advocates
program constructions using reusable components, libraries, and
fr`meworks. The pattern programming school stresses analogies to other
fields, such as architecture, by constructing programs using
well-designed and crafted solutions, or patterns, that recur in many
programming contexts.
What is Declarative Programming?
Declarative
programming separates the logic, or what, of an algorithm from the
control, or how, of an algorithm. The programmer still specifies the
logic or equations specifying the problem's relations, but the
programming system is responsible for control, or how the logic is
evaluated. The most familiar examples are spreadsheets and query
languages for relational databases. The user, or programmer, specifies a
mathematical relation as a query, say in SQL, for what to retrieve,
while the database engine determines how to execute the query against
the database.
There are many advantages to
declarative programming over the imperative style. In declarative
languages, programmers do not specify sequences of operations, but only
definitions or equations specifying relations. Unlike imperative
programming, the logic relations in declarative programming are
execution order independent, free of side effects of evaluation, and
semantically clear to visual inspection.
The
declarative family of programming languages has a long history in the
academic computer science community and specialized areas of commercial
application, such as compiler construction, expert systems, and
databases. Declarative languages have two main family trees. The logic
declarative languages, such as Prolog, are based on first-order
predicate calculus, which generalizes the notions of Aristotelian true
or false values to statements, or predicates, involving relations among
any entities. The other family branch consists of functional declarative
languages, such as Miranda, Haskell, and SML. The functional
declarative languages are based on the l-calculus developed by the
mathematician, Alonzo Church in the 1930's. l-calculus formalizes the
notions of recursive application of pure functions to computable
problems. Although not widely known as such, the latest programming
fashion, XSLT, an extensible stylesheet language for transforming XML,
is also a functional declarative language.
Despite
the theoretical advantages of declarative programming languages, they
do not have widespread use in commercial programming practice despite an
attempt in the 1980's by Borland to mass-market a PC version of Prolog
along with the highly popular Turbo Pascal. There are many factors
contributing to the infrequent use of declarative languages. A large
contributor is the paucity of collegiate training in declarative
languages, but awkward syntaxes of some languages, inefficient compilers
and run-times, and restricted domains of applicability of generalized
"how-to" mechanisms are all contributors.
Using Declarative Strategies in Commercial Software
While
declarative programming languages have not received wide-spread
commercial usage, the strategy of separating logic, or what, from
control, or how, in an algorithm is a powerful, generalized technique
for increasing ease of use and extending the longevity of software.
Declarative techniques are particularly powerful in user interfaces and
application programming interfaces (APIs) that have a rich, complex set
of inputs over a relatively small field of execution behaviors.
Two
examples of commercial software that illustrate the applicability of
declarative techniques are DriverLINX and ExceLINX in the fields of data
acquisition and test instrument control.
Using Declarations for Data Acquisition
DriverLINX
is an API for controlling data-acquisition hardware used to measure and
generate analog and digital signals interfaced to all types of external
transducers. Data-acquisition applications include laboratory research,
medical instrumentation, and industrial process control.
Traditionally,
APIs for data-acquisition devices modeled the characteristics of the
hardware design and had a large number of functions of one or more
parameters to setup the hardware and control data flow through the
system. The ordering of sequences of operations was often critical to
correctly programming and controlling the hardware. Upgrading to new
data-acquisition hardware was often costly as hardware-necessitated
changes in the order of operation sequences to program the hardware
required costly software changes.
To surmount
these problems, DriverLINX takes an abstract and declarative approach
to data-acquisition programming. Instead of modeling specific board
designs, DriverLINX abstracts the functional subsystems of
data-acquisition hardware into generalized attributes and capabilities.
Programs request the measurement task they want to perform by
parameterizing a "service request" declaration. The DriverLINX runtime
determines how to satisfy the service request using the available
hardware and returns the measurements as a packetized stream to the
program. The data-acquisition programmer is relieved of any
responsibility for data-acquisition algorithm control.
Besides
relieving the programmer of control responsibility, the DriverLINX
abstract, declarative approach gives the program syntactic and semantic
interchangeability when migrating to equivalent hardware products. The
abstract, declarative approach also helps isolate the software vendor
from early technological obsolescence of change in the computer industry
by focusing on the immutable logic of data-acquisition relations while
the control mechanisms vary with software developments. DriverLINX has
been a viable approach to data-acquisition programming for more than 12
years despite the market evolution from 16-bit Windows to .NET today.
Using Declarations for Test Instruments
Test
instruments, such as digital voltmeters and electrometers, have evolved
from simple devices with a front panel knob and display screen to
sophisticated measurement processors performing dozens of measurement
and control functions. Like data-acquisition devices, typically
developers send a carefully ordered sequence of commands to an
instrument to setup the measurement and then send additional command
sequences to control the data flow of measurements from the instrument.
The aforementioned problems for developers using imperative approaches
to instrument control significantly limit ease of use and prohibit quick
instrumentation solutions to short-term measurement needs.
ExceLINX
is an add-in to Microsoft Excel that allows rapid specification of
instrument test setups by using worksheet forms. Users specify, or
declare, the channels, configurations, sampling rates, triggering, and
data locations for the measurements they wish to perform by filling out
an Excel worksheet. When the user selects the "start" button on the
toolbar, ExceLINX translates the specification into the correct command
sequence for the target instrument, initiates the measurement, and flows
the data back to the requested worksheet. Users can setup and collect
measurements by themselves in minutes using logic specifications
compared to days or weeks using programmer's time for imperative
specifications.
Internally, ExceLINX also
uses a declarative approach to handling the complex problem of field
validation for the worksheet forms. Instruments have hundreds of
parameters with complex overlaps among parameters. To validate whether
the instrument supports the parameter set the user selected, ExceLINX
maintains a dependency tree of allowed, disallowed, and unused
parameters for every input cell on the worksheet. Each node in the tree
also maintains logical relations among the selected set of parameters
that ExceLINX evaluates at runtime to cross validate user input
selections. Each supported instrument model has different parameter
semantics, but ExceLINX can easily handle this complexity by switching
model trees because the model-specific logic in the validation tree is
separate from the shared control implementation in the ExceLINX code.
Declarative
programming strategies that separate logic from control in algorithms
are powerful techniques that can be used with today's popular imperative
languages. These techniques can make software more interchangeable,
maintainable, usable, and endurable.
No comments:
Post a Comment