[this is a bit long, maybe it would be better deleted and replaced
by a link to http://www.acooke.org/andrew/writing/scheme-eng.html ?]
before writing anything about software engineering in scheme i want
to say that i have used scheme hardly at all, lisp only a little, ml
only in small (home) projects, and have only recently had to worry
about the issues of software engineering (java) in a team with mixed
abilities. so i have no authority.
also, the following points are in the rather random order they
occurred to me while out running just half an hour ago. so this is
more to provoke discussion than present any deep truths...
patterns
i doubt that patterns are only for architects and java/c++
programmers. patterns are, imho, nothing more than guides to "good
practice". every discipline has good practice, even dadaism.
maybe you are referring to paul graham's comment about patterns.
if so, i think you need to re-read his article with a rather more
sceptical eye - there's a lot of snide and bitter point-scoring there,
imho, that deserved the aggressive response it received on lambda. it
would have been much better to say that in lisp the power of macros
allows you to adapt the language to better suit the patterns that
occur in particular application areas. from that point of view his
excellent "on lisp" can be seen as partly a catalogue of lisp patterns
and partly guidance on how to handle patterns in lisp.
so i'd suggest asking in a scheme related ng for advice on
appropriate scheme patterns. there was a thread a while back on clf
about patterns in functional languages, too.
jack of all trades
one of the big arguments for lisp (and scheme to a lesser extent)
is that it supports all styles of programming. the advantages
ascribed to this seems to contradict one of the big arguments for
great art - that it requires boundaries. certainly i enjoy working in
c (yes!), ml or python more than lisp or perl because they are
simpler. maybe i'm just stupid, but i find that a simpler language
helps focus the mind. perhaps more importantly, having just one way
to do it suggests that libraries and code are easier to understand
(although maybe more tedious, less succinct, elegant etc to use).
anyway, one of my problems when using lisp, and i suspect one of
yours in using scheme, is that you are dithering between approaches.
i suggest you choose one and stick with it. so what are the
options?
functional programming
state is the Big Problem. so get rid of it. you can use scheme to
write purely functional programs. in my experience that means that
you design programs by providing ways to iterate across data
structures (to both read and construct values). the modularity in the
program comes from the series of intermediate structures needed to get
from where you begin to where you want to arrive. the "interfaces" tend
to be folds, maps, etc.
for an example of good engineering within the functional paradigm,
imho, look at the papers describing erwig's functional graph
library.
one of the useful tools that makes this approach simpler is types.
some automated way of making sure that you are applying the right
function to the right datum is invaluable. since scheme and lisp do
not, usually, use static typing, but do have strong dynamic typing,
that suggests tests are the way to go. and so yes, i'm sure scheme
programmers do have unit tests - it's either that or prayer, as far as
i can see. again, ask on a scheme ng.
maybe one alternative to prayer is to introduce some kind of static
typing "yourself". in the lisp world, cmucl is always provided as the
example of a lisp where you can add static type declarations if you
want. there's also a commercial (but possibly freely available?)
lisp-like language out there that has static typing of some kind. i'm
pretty sure it's been mentioned on lambda before (sorry, can't
remember the name). and for scheme, there are also options - search
google (or lambda) for scheme and "soft typing".
something i should have mentioned earlier - avoid lists (at least
in public, if you see what i mean). obviously, lists are wonderful
things and when you start with functional languages you just can't get
enough of them. but you end up with every damn function working on
lists and, without static typing to describe what the list contains,
big possibilities for confusion.
so when you divide your problem up into data structures, with
appropriate functions for iterating over them, stick those structures
inside something hard and spiky. i don't know what's available in
scheme, but lisp certainly has named structures. it makes code easier
to read and even if you still get a runtime error at least it's
located where the problem is, rather than several function calls away
when you finally decide to process the value that you pulled of the
front of "some list".
one of the things java somehow managed to get at least half-right,
imho, is the separation between interface and implementation. it's
often a sign of lazy java code when an object is an interface. this
was hammered home last week when i lost half a day or more after
getting the signature wrong when implementing an "interface" - because
the interface was a (non-abstract) class, the compiler didn't complain
and my method wasn't called.
unfortunately, without static typing, scheme and lisp do seem to be
somewhat screwed in this area.
closures/oop
if you can't banish state, stick it in a closure. this is the
approach that sicp advocates and it's effectively oop (without
inheritance, but who needs that if you don't have interfaces? ;-).
lisp comes with support for oop, although it doesn't enforce data
hiding. i don't know what you do in a production environment to cure
that apart from code reviews and documentation. if you're paul
graham, of course, (or, in my experience, any long-term member of cll)
you work with the best programmers on god's green earth and it's
simply Not A Problem. but if you're condemned to working with mere
mortals, well....
for scheme, oo support has also been implemented by various people.
maybe this sounds like a cop-out, but the whole point of the language
is something simple and extensible, so objecting to extensions seems a
bit contradictory. again, i suspect they don't hide data.
i was going to write more, but seem to have suddenly run out of
ideas. i may add to this later. suspect all of the above is
obvious, and know some of the above includes flippancy, over
simplification and frustration that may offend more delicate lambda
readers, but hope it is of some help (in places) or, at least,
confirms (in other places) that the problems are inherent in the
language.
|