Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

common lisp style and design, engineering question

167 views
Skip to first unread message

ccc31807

unread,
May 21, 2013, 12:24:49 PM5/21/13
to
This is not a hypothetical question. I will be implementing some
applications in the next year or so, such as artificial neural
networks, support vector machines, Baysesian networks (inspired in
part by 'Programming Collective Intelligence' by Toby Segaran who
implements these in Python.)

My day-to-day language is Perl, and C to a lesser extent. I write
these in a procedural style, and usually begin by writing pseudo-code,
and then refining the pseudo-code, and finally by writing the
procedures to implement the requirements. A very simple example might
be like this:
#! perl
# 1. open documents and read data into memory
sub open_documents() { ... }
sub read_into_memory() { ... }
# 2. build and populate data structures
sub build_hashes() { ... }
# 3. munge data
sub calculate_values() { ... }
sub join_values() { ... }
sub calculate_sums_and_averages() { ... }
# 4. open out files and write data
sub open_report_files() { ... }
sub write_reports() { ... }
exit(0);

I've been collecting and reading through a number of Common Lisp
scripts, many of them shamelessly stolen from 'The Elements of
Artificial Intelligence Using Common LISP' by Steven L. Tanimoto, and
others. I've copied a snippet at the end of this post to illustrate my
question. I don't who the original author of this code was.

My problem in reading this code, and other Common Lisp code, is that I
don't have a framework to understand the code, and I have no help from
a procedural style design to help me understand it. I can understand
individual functions more or less (even those with uninformative
parameter names like d, i, p, and so on). I don't really think that
this is a problem with commenting the code -- I don't heavily comment
my own code and usually delete my pseudo-code scaffolding, relying on
the flow of the application to be self documenting. And this works
well!

Paul Graham has famously written that Lisp can be a better
specification language than a natural language. This may be so /IF/
you can understand the design of the application. Unfortunately, I
don't seem to be able to.

Here's the question: Are there design best practices for Common Lisp
in particular, and functional languages in general, similar to the top-
down, step-wise refinement development methodology taught in software
engineering courses? I don't mean 'similar' as in a particular
programming paradigm, but 'similar' in the sense of a standard set of
policies and procedures relating to design. I suspect there are not,
and I have looked -- but if there are, where are they?

My current level with CL is having been through the current books by
P. Selbel and C. Barski several times, 'ANSI Common Lisp' once, and
other publications. I have only written one respectfully sized
application in CL, and like the bottom up style where you
(interactively) solve the small problems and move on to the bigger
problems. Like PG says, you write the program both bottom-up and top-
down. My problem is that I haven't yet gotten a handle on the proper
design methodology to use that that I can understand my own code, and
perhaps understand code written by others.

Thanks, CC.

;;;;;;;;;;;sample CL code;;;;;;;;;;;;;;;;
(defun node-count (net layernum)
(length (get-layer net layernum)))

(defun layer-count (net)
(length (nnet-nodes net)))

(defun get-layer (net layernum)
(nth layernum (nnet-nodes net)))

(defun get-node (net layernum nodenum)
(nth nodenum (get-layer net layernum)))

(defun get-bias (net)
(nnet-bias net))
...
(defun get-weight-list (node)
(nnode-w node))

(defun get-nth-weights (layer n)
(cond
((endp layer) nil)
(t (nconc (list (get-weight (car layer) n))
(get-nth-weights (cdr layer) n)))))

(defun get-child-weights (net layernum nodenum)
(get-nth-weights (get-layer net (1+ layernum)) nodenum))

(defun get-parent-weights (net layernum nodenum)
(get-nth-weights (get-layer net (1- layernum)) nodenum))

Barry Margolin

unread,
May 21, 2013, 1:53:00 PM5/21/13
to
In article
<cfeafd2d-af40-4670...@i3g2000yqf.googlegroups.com>,
ccc31807 <cart...@gmail.com> wrote:

> Here's the question: Are there design best practices for Common Lisp
> in particular, and functional languages in general, similar to the top-
> down, step-wise refinement development methodology taught in software
> engineering courses? I don't mean 'similar' as in a particular
> programming paradigm, but 'similar' in the sense of a standard set of
> policies and procedures relating to design. I suspect there are not,
> and I have looked -- but if there are, where are they?

Lisp is an imperative, procedural language. What suggests you should
use a different design methodology than you use with Perl or C?

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Raymond Wiker

unread,
May 21, 2013, 3:52:33 PM5/21/13
to
Barry Margolin <bar...@alum.mit.edu> writes:

> In article
> <cfeafd2d-af40-4670...@i3g2000yqf.googlegroups.com>,
> ccc31807 <cart...@gmail.com> wrote:
>
>> Here's the question: Are there design best practices for Common Lisp
>> in particular, and functional languages in general, similar to the top-
>> down, step-wise refinement development methodology taught in software
>> engineering courses? I don't mean 'similar' as in a particular
>> programming paradigm, but 'similar' in the sense of a standard set of
>> policies and procedures relating to design. I suspect there are not,
>> and I have looked -- but if there are, where are they?
>
> Lisp is an imperative, procedural language. What suggests you should
> use a different design methodology than you use with Perl or C?

Possibly a number of eloquent proponents of bottom-up programming
instead of the top-down style that step-wise refinement entails?

Pascal J. Bourguignon

unread,
May 22, 2013, 2:13:51 AM5/22/13
to
Have a look at:
http://groups.google.com/group/comp.lang.lisp/msg/a827235ce7466a92


> I've been collecting and reading through a number of Common Lisp
> scripts, many of them shamelessly stolen from 'The Elements of
> Artificial Intelligence Using Common LISP' by Steven L. Tanimoto, and
> others. I've copied a snippet at the end of this post to illustrate my
> question. I don't who the original author of this code was.
>
> My problem in reading this code, and other Common Lisp code, is that I
> don't have a framework to understand the code, and I have no help from
> a procedural style design to help me understand it. I can understand
> individual functions more or less (even those with uninformative
> parameter names like d, i, p, and so on). I don't really think that
> this is a problem with commenting the code -- I don't heavily comment
> my own code and usually delete my pseudo-code scaffolding, relying on
> the flow of the application to be self documenting. And this works
> well!

That's idiotic. It's done by a lot of people, but that doesn't remove
that it's idiotic to throw away the source code from the binary^W text
you generated from the source code. Yes, I mean your so called "pseudo"
code. This is actually your source code!!!




> Paul Graham has famously written that Lisp can be a better
> specification language than a natural language. This may be so /IF/
> you can understand the design of the application. Unfortunately, I
> don't seem to be able to.


There's not really a difference between a specification and a design
(and a "source" code and a binary code), in the sense that
(theorically), there is a mechanical process to transform each form into
the next. So far, the early steps are done by human compilers and later
steps are done by programs, but eventually it'll all be done by programs.


The thing with "runnable pseudo code" (see url above), is that if you
just write your specification or your design in sexp form, then you can
write in lisp the program that transforms in into the next form.

Then your "source code" will be your specification or your design, and
it will explain by itself the application because it will BE itself the
application!



> Here's the question: Are there design best practices for Common Lisp
> in particular, and functional languages in general, similar to the top-
> down, step-wise refinement development methodology taught in software
> engineering courses?


If they're best practice in software engineering, what makes you think
they're not best practice writing Common Lisp programs? Is writing Lisp
programs not software engineering?


> I don't mean 'similar' as in a particular
> programming paradigm, but 'similar' in the sense of a standard set of
> policies and procedures relating to design. I suspect there are not,
> and I have looked -- but if there are, where are they?

Right there under your nose! Just apply what you learned in your
software engineering classes!!!




> My current level with CL is having been through the current books by
> P. Selbel and C. Barski several times, 'ANSI Common Lisp' once, and
> other publications. I have only written one respectfully sized
> application in CL, and like the bottom up style where you
> (interactively) solve the small problems and move on to the bigger
> problems. Like PG says, you write the program both bottom-up and top-
> down. My problem is that I haven't yet gotten a handle on the proper
> design methodology to use that that I can understand my own code, and
> perhaps understand code written by others.
>
> Thanks, CC.
>
> ;;;;;;;;;;;sample CL code;;;;;;;;;;;;;;;;
> (defun layer-count (net)
> (defun get-bias (net)
> (defun node-count (net layernum)
> (defun get-layer (net layernum)
> (defun get-node (net layernum nodenum)
> (defun get-child-weights (net layernum nodenum)
> (defun get-parent-weights (net layernum nodenum)
> (defun get-weight-list (node)
> (defun get-nth-weights (layer n)


So you have net objects with a bias attribute.
Net objects are in relationship with an indexed set of layer objects.
Layer objects are in relationship with an indexed set of node objects.
A node seems to have children and parents, and those relationships have
a weight attribute.


You don't need to read the body, just the signatures, to understand it.

--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.
You can take the lisper out of the lisp job, but you can't take the lisp out
of the lisper (; -- antifuchs

WJ

unread,
May 22, 2013, 5:34:14 PM5/22/13
to
Barry Margolin wrote:

> In article
> <cfeafd2d-af40-4670...@i3g2000yqf.googlegroups.com>,
> ccc31807 <cart...@gmail.com> wrote:
>
> > Here's the question: Are there design best practices for Common Lisp
> > in particular, and functional languages in general, similar to the top-
> > down, step-wise refinement development methodology taught in software
> > engineering courses? I don't mean 'similar' as in a particular
> > programming paradigm, but 'similar' in the sense of a standard set of
> > policies and procedures relating to design. I suspect there are not,
> > and I have looked -- but if there are, where are they?
>
> Lisp is an imperative, procedural language. What suggests you should
> use a different design methodology than you use with Perl or C?

Wrong. Lisp (Clojure) is a functional language.

CL is not Lisp.

ccc31807

unread,
May 23, 2013, 11:35:36 AM5/23/13
to
On May 22, 2:13 am, "Pascal J. Bourguignon" <p...@informatimago.com>
wrote:
Thanks for the link.

> > this is a problem with commenting the code -- I don't heavily comment
> > my own code and usually delete my pseudo-code scaffolding, relying on
> > the flow of the application to be self documenting. And this works
> > well!
>
> That's idiotic.  It's done by a lot of people, but that doesn't remove
> that it's idiotic to throw away the source code from the binary^W text
> you generated from the source code.  Yes, I mean your so called "pseudo"
> code.  This is actually your source code!!!

Maybe it is and maybe it isn't idiotic, but it works for me. Maybe I'm
the idiot!

> There's not really a difference between a specification and a design
> (and a "source" code and a binary code), in the sense that
> (theorically), there is a mechanical process to transform each form into
> the next.  So far, the early steps are done by human compilers and later
> steps are done by programs, but eventually it'll all be done by programs.

Depends on your point of view. If the specification is a requirements
document and the design is a design document, then in fact there's a
lot of difference. But I understand your point and agree with it.

> The thing with "runnable pseudo code" (see url above),  is that if you
> just write your specification or your design in sexp form, then you can
> write in lisp the program that transforms in into the next form.
> Then your "source code" will be your specification or your design, and
> it will explain by itself the application because it will BE itself the
> application!

This begs the question. OO design has been heavily formalized, books
have been written about OO design and courses taught, and I've taken
two of them so far. We can do class diagrams, state diagrams, sequence
diagrams, and the like. These diagrams can be directly translated into
OO templates, and the diagrams can be generated from OO code. However,
this doesn't seem to be true for FP code. Yes, if I wanted to write OO
Common Lisp, then UML and the OO design techniques would be valuable
if not mandatory. My question was directed toward writing FP code.

> > Here's the question: Are there design best practices for Common Lisp
> > in particular, and functional languages in general, similar to the top-
> > down, step-wise refinement development methodology taught in software
> > engineering courses?
>
> If they're best practice in software engineering, what makes you think
> they're not best practice writing Common Lisp programs?  Is writing Lisp
> programs not software engineering?

How would you go about generating design documents in CL? If I were
writing OO code, I would use UML, patterns, and all the other tricks.
If I were writing procedural code (which I do -- a lot), then I would
use flow charts and pseudocode. Both these activities are largely top
down. How do you create design documents bottom up? The best answer I
can come up with is that you design DSLs to solve basic problems, and
then use those to build your higher level code. However, this approach
seems to be entirely different that doing design work as we
customarily understand it.

> Right there under your nose!  Just apply what you learned in your
> software engineering classes!!!

If I could do that, I wouldn't have asked the question.


> So you have net objects with a bias attribute.
> Net objects are in relationship with an indexed set of layer objects.
> Layer objects are in relationship with an indexed set of node objects.
> A node seems to have children and parents, and those relationships have
> a weight attribute.

You are either suggesting to draw a control flow graphs, which
essentially amounts to a flow chart, or else to generate some kind of
network graph or tree. I do not know how to do this in the context of
program design.

> You don't need to read the body, just the signatures, to understand it.

So how do I understand this function signature?
(add-results-to-table (t r a n) [don't need to read the body to
understand the function] )

I said that this was not a hypothetical question. If I wanted to
design an artificial neural network in the functional style, how would
I go about doing it?

Thanks, CC.

Pascal J. Bourguignon

unread,
May 23, 2013, 3:12:00 PM5/23/13
to
ccc31807 <cart...@gmail.com> writes:

> On May 22, 2:13 am, "Pascal J. Bourguignon" <p...@informatimago.com>
> wrote:

>> The thing with "runnable pseudo code" (see url above),  is that if you
>> just write your specification or your design in sexp form, then you can
>> write in lisp the program that transforms in into the next form.
>> Then your "source code" will be your specification or your design, and
>> it will explain by itself the application because it will BE itself the
>> application!
>
> This begs the question. OO design has been heavily formalized, books
> have been written about OO design and courses taught, and I've taken
> two of them so far. We can do class diagrams, state diagrams, sequence
> diagrams, and the like. These diagrams can be directly translated into
> OO templates, and the diagrams can be generated from OO code. However,
> this doesn't seem to be true for FP code. Yes, if I wanted to write OO
> Common Lisp, then UML and the OO design techniques would be valuable
> if not mandatory. My question was directed toward writing FP code.

Remember the equivalence between objects and closures.


(let ((x 0)
(y 0))
(defun f (m &optional a)
(ecase m
(:set (check-type a integer) (setf y a))
(:inc (incf x) (decf y) (when (zerop y) 'bing))
(:get x))))

is the same as:

(defclass f ()
((x :initform 0 :accessor f-x)
(y :initform 0 :accessor f-y)))

(defmethod f-set ((f f) a)
(with-slots (x y) f
(check-type a integer)
(setf y a)))

(defmethod f-get ((f f))
(with-slots (x y) f
x))

(defmethod f-inc ((f f))
(with-slots (x y) f
(incf x) (decf y) (when (zerop y) 'bing)))


(progn
(f :set 3)
(list (f :inc) (f :inc) (f :inc) (f :inc) (f :get)))
--> (nil nil bing nil 4)


(let ((f (make-instance 'f)))
(f-set f 3)
(list (f-inc f) (f-inc f) (f-inc f) (f-inc f) (f-get f)))
--> (nil nil bing nil 4)


Of course, CLOS gives you some syntactic sugar and niceties, but nothing
you couldn't implement easily enough just with closures.



So, like a mathematician, you can answer all your questions about FP by
converting closures to objects, answering the question in OO, and
converting back the answer to closures.




>> > Here's the question: Are there design best practices for Common Lisp
>> > in particular, and functional languages in general, similar to the top-
>> > down, step-wise refinement development methodology taught in software
>> > engineering courses?
>>
>> If they're best practice in software engineering, what makes you think
>> they're not best practice writing Common Lisp programs?  Is writing Lisp
>> programs not software engineering?
>
> How would you go about generating design documents in CL? If I were
> writing OO code, I would use UML, patterns, and all the other tricks.

Just the same.

UML is not about Object Oriented Programming. It is about a Unified
MODELING Language. You can model anything with it. For example, the
metabolism of cells. http://www.ncbi.nlm.nih.gov/pubmed/15888343

So if you know some UML, you can describe a lisp program with it. Or a
specification. Or a design.



> If I were writing procedural code (which I do -- a lot), then I would
> use flow charts and pseudocode. Both these activities are largely top
> down. How do you create design documents bottom up? The best answer I
> can come up with is that you design DSLs to solve basic problems, and
> then use those to build your higher level code. However, this approach
> seems to be entirely different that doing design work as we
> customarily understand it.

Nobody forces you to write the design document before the
implementation, or to write the high level description before the low
level details. You can develop and write them in the order you want.
Just use small cycles to refine them a lot.


It's to be able to read them and understand a complex system more easily
that we start reading the specifications, then the design, then the
sources, and that we start by reading high level descriptions or parts
before reading the low level details. But this sorting is done only as
a last editing step, it's not a development process.





>> Right there under your nose!  Just apply what you learned in your
>> software engineering classes!!!
>
> If I could do that, I wouldn't have asked the question.
>
>> So you have net objects with a bias attribute.
>> Net objects are in relationship with an indexed set of layer objects.
>> Layer objects are in relationship with an indexed set of node objects.
>> A node seems to have children and parents, and those relationships have
>> a weight attribute.
>
> You are either suggesting to draw a control flow graphs, which
> essentially amounts to a flow chart, or else to generate some kind of
> network graph or tree. I do not know how to do this in the context of
> program design.

No, I'm suggesting that a set of function signatures define one or more
abstract data types, and that an ADT is just an OO class.

What I'm saying is that I see no difference between:

> (defun layer-count (net)
> (defun get-bias (net)
> (defun node-count (net layernum)
> (defun get-layer (net layernum)
> (defun get-node (net layernum nodenum)
> (defun get-child-weights (net layernum nodenum)
> (defun get-parent-weights (net layernum nodenum)
> (defun get-weight-list (node)
> (defun get-nth-weights (layer n)


(defclass net (…) (…))
(defclass layer (…) (…))
(defclass node (…) (…))
(defmethod get-bias ((self net)) …)
(defmethod get-layer ((self net) layernum) …)
(defmethod node-count ((self layer)) …)
(defmethod get-node ((self layer) nodenum) …)
(defmethod get-child-weights ((self node)) …)
(defmethod get-parent-weights ((self node)) …)
(defmethod get-weight-list ((self node)) …)
(defmethod get-nth-weights ((self layer) n) …)

>> You don't need to read the body, just the signatures, to understand it.
>
> So how do I understand this function signature?
> (add-results-to-table (t r a n) [don't need to read the body to
> understand the function] )

It is bad code, since the parameter names are not explicit. It should be
rejected from the code review.


> I said that this was not a hypothetical question. If I wanted to
> design an artificial neural network in the functional style, how would
> I go about doing it?

Natural neural network use the physical world which is full of state and
mutation. It is silly to try to implement them in the functional
(mutation-free) style. You can still do it, considering that each
change of state in the neural network actually forks a new copy of the
neural network with a few differences. Once you do that, there's no
difference between functional programming and the other mutation-full
programming styles.

In any case, see above. You have a set of function signatures that
works perfectly well in the functional style, and they're equivalent to
the classes and methods I described.

Paul Wallich

unread,
May 25, 2013, 4:32:05 PM5/25/13
to
On 5/23/13 3:12 PM, Pascal J. Bourguignon wrote:
[...]
> Natural neural network use the physical world which is full of state and
> mutation. It is silly to try to implement them in the functional
> (mutation-free) style. You can still do it, considering that each
> change of state in the neural network actually forks a new copy of the
> neural network with a few differences. Once you do that, there's no
> difference between functional programming and the other mutation-full
> programming styles.

Some folks actually did this back in the 80s, and reported that the big
difference was the ability to wrap programmers' brains and certain kinds
of analytical software around the work they were doing.

Not a neural network, but another kind of stateful knowledge
representation, and each functional application returned a new world
with a few changes from the old world (with oodles of stuff under the
hood to make it work cheaply).

Some algorithms can be most easily expressed/composed in functional form.
0 new messages