Thu Jun 11 12:21:17 CEST 2009
Code vs. Data (abstract or concrete instructions?)
In Scheme, I am tempted to use code instead of data as the result of
functions. This is akin to an actor style of programming.
Instead of letting functions return data that needs to be interpreted
by the caller, have them return behaviour: abstract entities that only
leave the freedom of execution time, not freedom of interpretation.
This can lead to some very concise code, but when used over-zealously
it can complicate matters. Sometimes intermediate data stucutures
that require (centralized, explicit) interpretation are easier to
understand than objects that delegate functionality behind the scenes.
This seems to be about two conflicting types of abstraction: message
passing (GOTO with arguments/continuations), vs static descriptions =
concrete representation based approach.
This popped up in the construction of a dataflow language (DFL)
compiler, which transforms a parallel description of nodes and
processes into a serialized runnable Scheme form. I was throwing
abstract (lambda () ...) all over the place until I realized that
maybe an abstract data structure to be interpreted later was a simpler
approach. Often however, the abstract approach is better as it allows
to specify some functionality locally to where the decision needs to
be made, instead of in a central place.
Looks like this is very much related to the "dependency injection"
principle. If you _can_ do it concretely, please do as this is often
easier to understand. However, sometimes things need to be abstracted
out and decided elsewhere. Late binding can be useful (and when used
as a central organizing principle it can be quite powerful). But
I'd like to say in general: don't make it too flexible!
Note that abstraction can be a (resource use) optimization. I.e. in
Forth, explicit data structures are difficult to handle due to lack of
local namespace support and lack of automatic memory management.
Implementing those would be a prohibitive complexity explosion going
agains the grain of the "simplicity" module. Representing things as
behaviour instead of data structures is usually a valuable and
efficient option. In Forth, memory is usually best managed where it
is used, which means stacks or dynamic variables (which are
essentially stacks). This management is then easily hidden behind a
procedure. This is "don't set a flag, set the data" from Leo Brodie's
"Thinking Forth". Or is this quote from Dijkstra? I can't
seem to find the original reference.
This is related to Chuck Moore's early binding philosophy. Aiming
for minimalism, the best strategy is to not leave things open for
(re)interpretation. Minimalism makes rewriting easier: so you aim for
full re-implementation instead of trying to anticipate future