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

Maintaining multivariable state (newbie question)

85 views
Skip to first unread message

Erik R.

unread,
Aug 24, 2007, 9:36:45 AM8/24/07
to
Greetings all. I'm writing a simple min-max search program to
practice my lisp. I need 3 or 4 variables to represent the current
state and then a few functions to change state and get possible future
states. My background is in java, so my first instinct was to make a
CLOS object with slots and methods. This quickly turned into a mess
with every method starting with with-slots to make all the slot
variables local and accessible. I'm certain that this is a horribly
disgusting approach in lisp and I was wondering what the proper way to
go about this is.

Do I just defvar all my state variables? It seems disgusting to make
them all global like that when I really only want to access them from
a handful of methods.

Do I surround my functions with a let that defines the variables?

A defstruct that I pass as a parameter to all my functions?

I'm sure this is a really silly newbie question, but it's just not
obvious, and none of the lisp books I've read address stuff like this.

Cheers,
Erik

patzy

unread,
Aug 24, 2007, 10:35:18 AM8/24/07
to

Erik R.

unread,
Aug 24, 2007, 10:47:27 AM8/24/07
to

So I define them in a let around my defuns?

Pascal Bourguignon

unread,
Aug 24, 2007, 11:42:54 AM8/24/07
to
"Erik R." <rasmus...@gmail.com> writes:
> Greetings all. I'm writing a simple min-max search program to
> practice my lisp. I need 3 or 4 variables to represent the current
> state and then a few functions to change state and get possible future
> states. My background is in java, so my first instinct was to make a
> CLOS object with slots and methods. This quickly turned into a mess
> with every method starting with with-slots to make all the slot
> variables local and accessible. I'm certain that this is a horribly
> disgusting approach in lisp and I was wondering what the proper way to
> go about this is.

Note, as disgusting as it may seem, it may still be quite efficient.
With a good implementation of CLOS, WITH-SLOTS can be as lightweight
as a LET, I think.

Anyways, the disgusting stuff you can hide it:

(defmacro define-method/slots (name arguments slots instance &body body)
`(defmethod ,name ,arguments
(with-slots ,slots ,instance
,@body)))

[Well you might want to process body more sophistically to put the
documentation string and the declarations in the right place].

(define-method/slots example ((self my-class) arg)
(my-first-slot my-other-slot) self
(setf my-first-slot (* 2 my-other-slot)))

You could even make a more sophisticated macro to avoid repeatitively
giving the slots:

(with-slots/methods (my-first-slot my-other-slot) self

(defmethod method-1 ((self my-class) arg...)
(setf my-first-slot (* 2 my-other-slot)))

(defmethod method-2 ((other other-class) (self my-class) arg...)
(setf my-first-slot (* 3 my-other-slot)))

...)


with:


(defmacro with-slots/methods (slots instance &body body)
;; All form in BODY must be a DEFMETHOD
`(progn
,@(mapcar (lambda (defmet)
(destructuring-bind (defmethod name arguments &rest body) defmet
(assert (eq 'defmethod defmethod))
`(defmethod ,name ,arguments
(with-slots ,slots ,instance ,@body))))
body)))


> Do I surround my functions with a let that defines the variables?

That'd make a closure. Closures and objects are "equivalent".

> A defstruct that I pass as a parameter to all my functions?

There's no WITH-FIELDS, but you can write one... (You cannot use
with-slots on structures because the behavior of slot-value is not
specified for structures).

--
__Pascal Bourguignon__ http://www.informatimago.com/

READ THIS BEFORE OPENING PACKAGE: According to certain suggested
versions of the Grand Unified Theory, the primary particles
constituting this product may decay to nothingness within the next
four hundred million years.

Matthias Buelow

unread,
Aug 24, 2007, 1:26:23 PM8/24/07
to
Erik R. <rasmus...@gmail.com> wrote:

> Do I just defvar all my state variables? It seems disgusting to make

You can pass the state as argument(s) (if practical) for a more
functional approach... that's what I like doing although many here are
really into setq/setf.

If you're coming from Java, you probably need to unlearn that
"everything-in-the-world-is-an-object" nonsense first.

Ken Tilton

unread,
Aug 24, 2007, 3:25:45 PM8/24/07
to

Matthias Buelow wrote:
> Erik R. <rasmus...@gmail.com> wrote:
>
>
>>Do I just defvar all my state variables? It seems disgusting to make
>
>
> You can pass the state as argument(s) (if practical) for a more
> functional approach... that's what I like doing although many here are
> really into setq/setf.

The use case described by the OP is a good one for special variables.
Those are not as unclean as classic globals. Piling things into argument
lists is a slippery slope as the code gets more complex and you are
forever having to add them to arg lists of new functions.

kt

--
http://www.theoryyalgebra.com/

"We are what we pretend to be." -Kurt Vonnegut

Ken Tilton

unread,
Aug 24, 2007, 3:34:31 PM8/24/07
to

Erik R. wrote:
> Greetings all. I'm writing a simple min-max search program to
> practice my lisp. I need 3 or 4 variables to represent the current
> state and then a few functions to change state and get possible future
> states. My background is in java, so my first instinct was to make a
> CLOS object with slots and methods. This quickly turned into a mess
> with every method starting with with-slots to make all the slot
> variables local and accessible.

You are coming from Java and /anything/ looks like a mess? Methinks Lisp
has taken hold of you already. Happy thought,

> I'm certain that this is a horribly
> disgusting approach in lisp and I was wondering what the proper way to
> go about this is.
>
> Do I just defvar all my state variables? It seems disgusting to make
> them all global like that when I really only want to access them from
> a handful of methods.

Make up your frickin mind, will you? :) It looks as if you are halfway
on your Road. Look, you can have it either way, pile them into a struct
and pass it around or use specials. You can pile them into a struct and
bind /that/ to one special. You can run around on the street butt naked
quacking like a duck. This is Lisp, a multi-fetish language. I digress.

If it helps, specials are not your typical filthy globals. They are
accessible only during the runtime lifetime of their binding, to all the
code reached during that binding, which is precisely what you want.

>
> Do I surround my functions with a let that defines the variables?

It just binds them. Hmmm, I guess this could be a place for (declare
(special *hi-mom*)), but I myself have never played with that.

>
> A defstruct that I pass as a parameter to all my functions?
>
> I'm sure this is a really silly newbie question, but it's just not
> obvious, and none of the lisp books I've read address stuff like this.

I think books are hard enough to write. Paul Graham took a nice crack at
writing a style guide with On Lisp, but he is a rare cat.

hth,kt

Frank Goenninger DG1SBG

unread,
Aug 24, 2007, 3:38:55 PM8/24/07
to
Ken Tilton <kenny...@optonline.net> writes:

> Make up your frickin mind, will you? :) It looks as if you are halfway
> on your Road. Look, you can have it either way, pile them into a
> struct and pass it around or use specials. You can pile them into a
> struct and bind /that/ to one special. You can run around on the
> street butt naked quacking like a duck. This is Lisp, a multi-fetish
> language. I digress.

Wow - another one to be "fortune-filed" into kenny-fortunes.text ;-)

Frank
--

Frank Goenninger

frgo(at)mac(dot)com

"Don't ask me! I haven't been reading comp.lang.lisp long enough to
really know ..."

Rob St. Amant

unread,
Aug 24, 2007, 3:50:24 PM8/24/07
to
"Erik R." <rasmus...@gmail.com> writes:

I'm not sure how special variables address your problem. For all but
the simplest search algorithms, you'll need to maintain a potentially
large set of distinct states that you'll be generating and moving
between. It's reasonable to encapsulate the information about a state
in some way, and structures or class instances are a natural way to do
it. These get passed to your search algorithm, with relevant
information pulled out by appropriate accessor methods or functions.

In case you haven't checked it out already, Peter Norvig's Paradigms
of AI Programming is a good source for information about Lisp code and
program design for AI problems. There's an implementation of minimax
search in his Othello game player that you may find helpful.

Matthias Buelow

unread,
Aug 24, 2007, 4:09:53 PM8/24/07
to
Ken Tilton <kenny...@optonline.net> wrote:

> The use case described by the OP is a good one for special variables.
> Those are not as unclean as classic globals.

Special variables are a poor and obscure alternative to functional
arguments and shouldn't be used in place of them.

> Piling things into argument
> lists is a slippery slope as the code gets more complex and you are
> forever having to add them to arg lists of new functions.

What isn't a slippery slope in programming.

Erik R.

unread,
Aug 24, 2007, 5:55:40 PM8/24/07
to
On Aug 24, 10:09 pm, Matthias Buelow <m...@incubus.de> wrote:

Too true. Thanks to all of you. I can see that it's not completely
as clear-cut as I thought. It depends on how object oriented or how
functional I want to keep things.

Cheers,
Erik

Ken Tilton

unread,
Aug 24, 2007, 8:30:50 PM8/24/07
to

Matthias Buelow wrote:
> Ken Tilton <kenny...@optonline.net> wrote:
>
>
>>The use case described by the OP is a good one for special variables.
>>Those are not as unclean as classic globals.
>
>
> Special variables are a poor and obscure alternative to functional
> arguments and shouldn't be used in place of them.

Mathias, you ignorant slut. If you /can/ use a special variable atop a
call tree, then there is a helluva of a good chance it is less obscure
to use a special, and passing the data around as your cherished "pure
functional" parameters is nothing but an artifice of your enslavement to
purism. If the info is being passed as an argument, I am being told that
any given call might be supplied with a different place. If I see a
special, I understand it will be bound one place and that the call tree
will be operating on one place (yes, I can do a spiritual interrupt and
rebind the special in an emergency, but nothing has change: we have
bound the special one place and an entire call tree is now wailing away
at it).

Shouldn't you be over at comp.lang.functional?

>
>
>>Piling things into argument
>>lists is a slippery slope as the code gets more complex and you are
>>forever having to add them to arg lists of new functions.
>
>
> What isn't a slippery slope in programming.

Oh, my. You do program don't you? If so, I would hate to see it. I am a
programming god for one reason: when I sense the slightest drop-off in
productivity from my latest bit of code, out it comes. I cannot reliably
write good code, but I can reliably know when it has gotten unpleasant
to work with my code.

kenny

Ken Tilton

unread,
Aug 24, 2007, 9:48:05 PM8/24/07
to

Matthias Buelow wrote:
> Ken Tilton <kenny...@optonline.net> wrote:
>
>
>>The use case described by the OP is a good one for special variables.
>>Those are not as unclean as classic globals.
>
>
> Special variables are a poor and obscure alternative to functional
> arguments and shouldn't be used in place of them.

I meant to ask, when /should/ they be used?

Rob Warnock

unread,
Aug 24, 2007, 10:44:41 PM8/24/07
to
Ken Tilton <kent...@gmail.com> wrote:
+---------------

| Matthias Buelow wrote:
| > Ken Tilton <kenny...@optonline.net> wrote:
| >>The use case described by the OP is a good one for special variables.
| >>Those are not as unclean as classic globals.
| >
| > Special variables are a poor and obscure alternative to functional
| > arguments and shouldn't be used in place of them.
|
| I meant to ask, when /should/ they be used?
+---------------

For me, the two classic use cases are:

1. Things that work more-or-less like the ANSI-defined specials
such as *READ-BASE*, that is, large numbers of miscellaneous
global parameters that you *usually* want to just leave alone
but sometimes want to rebind for the dynamic extent of one
subtree of function calls.

Thanks *goodness* for specials for this case!! Passing the ~20 (!)
or so specials that affect the Lisp printer [see CLHS 22.1.1.1]
around in every function call just to enforce some kind of
functional "purity" would simply be insane.

2. Things that hold "this request" (or things *about* "this request")
for request/response protocols, especially when using some kind
of threading so that there are multiple "this" objects live at
any one instant. E.g., my personal web infrastructure has a special
named *HTTP-REQUEST*, a structure of type HTTP-REQUEST (duh!) that
holds all the gunk related to one -- freshly bound by the thread
that gets spawned upon each new ACCEPT on port 80. As the request
goes through the steps of its processing, various additional bits
of gunk get added to (or deleted from) the HTTP-REQUEST object.
After responding, the thread dies, and on its way out *HTTP-REQUEST*
gets unbound and the object gets GC'd, eventually. [No, I don't try
to re-use them, though I do (sometimes) re-cycle the connections
they had open to PostgreSQL.]

Another similar, though non-threaded, case is a discrete
event simulator, where specials with names like *CURRENT-TIME*,
*CURRENT-PROCESS*, *CURRENT-EVENT*, *CURRENT-NET*, *CURRENT-
DRIVER*, etc., may get bound & re-bound by the simulator's
main event loop [possibly a *very* large number of times
for one tick of *CURRENT-TIME*!], so that any given instance
of an object being simulated has a lexically-constant but
dynamically-varying environment. [Yes, Kenny, such things
should probably all be re-written in Cells. Humor me.]


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

Scott Burson

unread,
Aug 25, 2007, 2:38:24 AM8/25/07
to
On Aug 24, 6:36 am, "Erik R." <rasmussene...@gmail.com> wrote:

> A defstruct that I pass as a parameter to all my functions?

This would be my choice. Standard objects (what you get when you use
DEFCLASS) have overhead for functionality you don't need; this is just
a tuple, it sounds like, not a full-blown class. I try to avoid
specials most of the time, and anyway they don't sound like the right
thing for your situation, for reasons others have given.

Copying a small struct to change one slot (if you choose to do it that
way) is quite fast.

-- Scott

Scott Burson

unread,
Aug 25, 2007, 2:53:33 AM8/25/07
to
On Aug 24, 7:44 pm, r...@rpw3.org (Rob Warnock) wrote:
> For me, the two classic use cases [for specials] are:

>
> 1. Things that work more-or-less like the ANSI-defined specials
> such as *READ-BASE* [...]
>
> Thank *goodness* for specials for this case!! Passing the ~20 (!)

> or so specials that affect the Lisp printer [see CLHS 22.1.1.1]
> around in every function call just to enforce some kind of
> functional "purity" would simply be insane.

That would be insane, but having a printer class with these specials
turned into instance variables would be quite reasonable. All you
need is a copy-with-variations method that takes an existing instance
and a bunch of keyword parameters, and returns a new instance that has
been appropriately modified. If one of its instance variables is the
stream it writes to, then we would pass it around in place of a stream
-- something we are already used to passing around.

Specials are also solving a different problem: that the REPL has
state, and we need a way to access and modify that state. For this
use I think specials are fine. But this is a pretty limited use case.

> 2. Things that hold "this request" (or things *about* "this request")
> for request/response protocols

Here I'd rather pass the object around as an argument. Just a matter
of taste, I guess.

-- Scott

Sacha

unread,
Aug 25, 2007, 3:13:02 AM8/25/07
to
Matthias Buelow wrote:
> Ken Tilton <kenny...@optonline.net> wrote:
>
>> The use case described by the OP is a good one for special variables.
>> Those are not as unclean as classic globals.
>
> Special variables are a poor and obscure alternative to functional
> arguments and shouldn't be used in place of them.
>

I think that dynamic extent is conceptually very close to hidden
functional arguments. With the added benefit that you can setf these in
addition to regular let bindings.

These dynamic vars are not merely C constants. The dynamic extent is a
pretty nice feature, let's use it !

Sacha

Ken Tilton

unread,
Aug 25, 2007, 5:24:13 AM8/25/07
to

Rob Warnock wrote:
> Ken Tilton <kent...@gmail.com> wrote:
> +---------------
> | Matthias Buelow wrote:
> | > Ken Tilton <kenny...@optonline.net> wrote:
> | >>The use case described by the OP is a good one for special variables.
> | >>Those are not as unclean as classic globals.
> | >
> | > Special variables are a poor and obscure alternative to functional
> | > arguments and shouldn't be used in place of them.
> |
> | I meant to ask, when /should/ they be used?
> +---------------
>
> For me, the two classic use cases are:
>
> 1. Things that work more-or-less like the ANSI-defined specials
> such as *READ-BASE*, that is, large numbers of miscellaneous
> global parameters that you *usually* want to just leave alone
> but sometimes want to rebind for the dynamic extent of one
> subtree of function calls.
>
> Thanks *goodness* for specials for this case!! Passing the ~20 (!)

> or so...

That's a cop-out! The interesting question arises when there is /one/
variable to pass around, such as GrafPort in QuickDraw. Can I set it and
forget it, or do I have to pass it around so FillRect retains its
functional purity as Matthias demands? The Mac would have lasted about a
month with him as Chief Software Architect...

I think he must be a refugee from c.l.functional who got fed up with
functional purity without realizing why.

Matthias Buelow

unread,
Aug 25, 2007, 8:20:35 AM8/25/07
to
Ken Tilton <kenny...@optonline.net> wrote:

> I meant to ask, when /should/ they be used?

There are some exceptional situations when they come in handy, don't
they. Like for providing a rarely used parameter that changes the
default behaviour of some mechanism deep in the bowels of some call tree
(where passing keywords down the whole thing isn't really practical and
positional parameters even less so). But not as an ordinary state
passing mechanism.

Yrs trly,
Slut.

Robert Uhl

unread,
Aug 25, 2007, 11:38:45 AM8/25/07
to
Scott Burson <FSet...@gmail.com> writes:

> On Aug 24, 7:44 pm, r...@rpw3.org (Rob Warnock) wrote:
>> For me, the two classic use cases [for specials] are:
>>
>> 1. Things that work more-or-less like the ANSI-defined specials
>> such as *READ-BASE* [...]
>>
>> Thank *goodness* for specials for this case!! Passing the ~20 (!)
>> or so specials that affect the Lisp printer [see CLHS 22.1.1.1]
>> around in every function call just to enforce some kind of
>> functional "purity" would simply be insane.
>
> That would be insane, but having a printer class with these specials
> turned into instance variables would be quite reasonable.

Ah, but if most of the time the defaults are fine, isn't cleaner to just
call (FOO BAR) than (FOO BAR context)? And of course, one has to
retrieve the context, so really it's the difference between (FOO BAR)
and (FOO BAR (get-FOO-context)). Or the default context could be a
global variable, probably constant: (FOO BAR +FOO-context+).

> All you need is a copy-with-variations method that takes an existing
> instance and a bunch of keyword parameters, and returns a new instance
> that has been appropriately modified.

So:

(let ((context (copy-context=with-variations +FOO-context+
:add-1 t
:colour nil)))
(FOO BAR context))

is cleaner than:

(let ((*FOO-add-1* t)
(*FOO-colour nil))
(FOO BAR))

Perhaps our aesthetic senses are different...

--
Robert Uhl <http://public.xdi.org/=ruhl>
Customs officers enter into a Faustian bargain whereby they are given
absolute power in exchange for their sense of humour. Hitler's dad, you
will remember noddingly, was a Customs officer. And _Hitler_ thought he
was a nasty piece of work. --Mil Millington

Pascal Bourguignon

unread,
Aug 25, 2007, 12:22:45 PM8/25/07
to
Matthias Buelow <m...@incubus.de> writes:

There must be some globalness involved.


If you have only one "primitive" call that use it, like:

(let ((*print-base* 8))
(print 42))

it's hardly worth it, you could as well write:

(write 42 :base 8)


It's worthwhile to use a CL special variable because it's a global
variable that will apply on ALL the functions that need it:

(let ((*print-base* 8))
(loop for i from 0 to 20 do
(princ i) (princ ": ") (do-something-including-some-printing i)))

Using a special variable to store local state is not good style, when
you have in CL so many better alternatives.

--
__Pascal Bourguignon__ http://www.informatimago.com/

I need a new toy.
Tail of black dog keeps good time.
Pounce! Good dog! Good dog!

Russell McManus

unread,
Aug 25, 2007, 8:18:38 PM8/25/07
to
Scott Burson <FSet...@gmail.com> writes:

> That would be insane, but having a printer class with these specials
> turned into instance variables would be quite reasonable. All you
> need is a copy-with-variations method that takes an existing instance
> and a bunch of keyword parameters, and returns a new instance that has
> been appropriately modified. If one of its instance variables is the
> stream it writes to, then we would pass it around in place of a stream
> -- something we are already used to passing around.

I don't agree. Assume I want to print things out in hex by default.
I have a function that I call, that in turn calls other functions that
print things out. The first function doesn't do any printing, and
does not accept a printer context object as argument. How do I pass
the copied-with-variations print context instance down to the inner
level functions?

-russ

Scott Burson

unread,
Aug 25, 2007, 8:31:44 PM8/25/07
to
On Aug 25, 8:38 am, Robert Uhl <eadmun...@NOSPAMgmail.com> wrote:
> So:
>
> (let ((context (copy-context=with-variations +FOO-context+
> :add-1 t
> :colour nil)))
> (FOO BAR context))
>
> is cleaner than:
>
> (let ((*FOO-add-1* t)
> (*FOO-colour nil))
> (FOO BAR))
>
> Perhaps our aesthetic senses are different...

Perhaps they are, and reasonable people certainly can differ about
such things, but let me give some reasons why I lean toward the first
version even though it's a little more verbose:

() The dataflow relationship is explicit. I know that FOO depends on
the context in some way (and if I look at the body of FOO I can
similarly see immediately which of its callees have the same
dependence). In the second case, it's not as easy to trace the
dataflow -- well, in this example, the scope of the LET contains only
the call to FOO, but in general LET bodies are not always so simple,
and anyway the same applies to the body of FOO, the bodies of its
callees, etc.

() If I ever need to delay some operation by wrapping it in a lambda
and calling the resulting function later, the first form will behave
predictably and the second won't.

-- Scott

Rob Warnock

unread,
Aug 25, 2007, 9:21:50 PM8/25/07
to
Scott Burson <FSet...@gmail.com> wrote:
+---------------

| Robert Uhl <eadmun...@NOSPAMgmail.com> wrote:
| > So: (let ((context (copy-context=with-variations +FOO-context+
| > :add-1 t
| > :colour nil)))
| > (FOO BAR context))
| > is cleaner than:
| > (let ((*FOO-add-1* t)
| > (*FOO-colour nil))
| > (FOO BAR))
...

| () If I ever need to delay some operation by wrapping it in a lambda
| and calling the resulting function later, the first form will behave
| predictably and the second won't.
+---------------

Sure it will, if you wrap the LAMBDA around the binding form too:

(lambda ()


(let ((*FOO-add-1* t)
(*FOO-colour nil))

(FOO BAR)))

Or if you're worried about more parameters than those, you could
always do something along these lines [which I confess I have done
on certain rare occasions]:

(let ((later-add-1 t)
(later-colour nil)
(later-style *foo-style*)
(later-font *foo-font*)
(later-weight *foo-weight*))
(lambda (bar)
(let ((*foo-add-1* later-add-1)
(*foo-colour* later-colour)
(*foo-style* later-style)
(*foo-font* later-font)
(*foo-weight* later-weight))
(foo bar))))

Scott Burson

unread,
Aug 25, 2007, 11:19:52 PM8/25/07
to
On Aug 25, 6:21 pm, r...@rpw3.org (Rob Warnock) wrote:

> Scott Burson <FSet....@gmail.com> wrote:
> +---------------
> | Robert Uhl <eadmun...@NOSPAMgmail.com> wrote:
> | > So: (let ((context (copy-context=with-variations +FOO-context+
> | > :add-1 t
> | > :colour nil)))
> | > (FOO BAR context))
> | > is cleaner than:
> | > (let ((*FOO-add-1* t)
> | > (*FOO-colour nil))
> | > (FOO BAR))
> ...
> | () If I ever need to delay some operation by wrapping it in a lambda
> | and calling the resulting function later, the first form will behave
> | predictably and the second won't.
> +---------------
>
> Sure it will, if you wrap the LAMBDA around the binding form too:
>
> (lambda ()
> (let ((*FOO-add-1* t)
> (*FOO-colour nil))
> (FOO BAR)))

I meant, if I needed to lambda-abstract something somewhere in the
guts of FOO -- if the function thus created could be called after FOO
returned, I would have to remember to do something like what you show
below:

> Or if you're worried about more parameters than those, you could
> always do something along these lines [which I confess I have done
> on certain rare occasions]:
>
> (let ((later-add-1 t)
> (later-colour nil)
> (later-style *foo-style*)
> (later-font *foo-font*)
> (later-weight *foo-weight*))
> (lambda (bar)
> (let ((*foo-add-1* later-add-1)
> (*foo-colour* later-colour)
> (*foo-style* later-style)
> (*foo-font* later-font)
> (*foo-weight* later-weight))
> (foo bar))))

I think "confess" is the operative word here :)

Seriously -- if I found myself needing to do this, I would definitely
want to switch to using an instance.

-- Scott

Scott Burson

unread,
Aug 25, 2007, 11:50:47 PM8/25/07
to
On Aug 25, 5:18 pm, Russell McManus <russell_mcma...@yahoo.com> wrote:
> Assume I want to print things out in hex by default.
> I have a function that I call, that in turn calls other functions that
> print things out. The first function doesn't do any printing, and
> does not accept a printer context object as argument. How do I pass
> the copied-with-variations print context instance down to the inner
> level functions?

I think you've contradicted yourself. You say the first function
doesn't do any printing, but it calls other functions that do. Yet
viewed in terms of its contract, either this first function is
supposed to print something or it isn't. If it is, the fact that it
does so only indirectly, by calling other functions, is irrelevant --
it still has printing as part of its contract, and therefore I don't
have a problem with making it pass around a printer context.

Debugging printout is obviously a different case -- you want your
function to print something, but that's not part of its contract --
and I don't have a problem with using specials interactively to assist
in debugging.

But, you might say, what about a general-purpose higher-order function
like, say, MAPC? It doesn't already pass around a printer context,
and certainly shouldn't be made to do so. Right -- and that's exactly
why you want to pass it a closure that knows about the printer context
and whatever else is relevant to that particular MAPC call.

Look, I'm well aware that in the course of exploratory programming or
even normal maintenance, there are situations where adding a special
is a quick solution to a problem; and sometimes expediency is the
important thing. But in my experience, if you can take the time to
refactor your code, making the piece of information that you wanted to
pass via a special instead be an attribute of some appropriate object,
I believe you will very likely wind up with a higher-quality, more
maintainable piece of code.

-- Scott

Ken Tilton

unread,
Aug 26, 2007, 12:47:39 AM8/26/07
to

Matthias Buelow wrote:
> Ken Tilton <kenny...@optonline.net> wrote:
>
>
>>I meant to ask, when /should/ they be used?
>
>
> There are some exceptional situations when they come in handy, don't
> they. Like for providing a rarely used parameter that changes the

> default behaviour of some mechanism deep in the bowels of some call tree...

Oh, great, you figured out the reductio adsurbum case, and conceded that
not all state seen by a function is a parameter to the functionality.

Now all you have to do is (a) learn how to program (b) do so for thirty
years and then (c) try to come up with definition of where the line
falls when deciding "special or parameter". Apparently you acknowledge
already that overwrought scenery chewing with language like "ugly" and
"impure" are fine for Usenet posturing, not so helpful for actually
building software.

meanwhile, please explain how structured programming is a slippery slope
inevitably leading to unmaintainable code. And relational design, and
CLOS. And finite state machines. And why am I wasting time on a nincompoop?

Russell McManus

unread,
Aug 26, 2007, 12:10:07 PM8/26/07
to
Scott Burson <FSet...@gmail.com> writes:

> On Aug 25, 5:18 pm, Russell McManus <russell_mcma...@yahoo.com> wrote:
>> Assume I want to print things out in hex by default.
>> I have a function that I call, that in turn calls other functions that
>> print things out. The first function doesn't do any printing, and
>> does not accept a printer context object as argument. How do I pass
>> the copied-with-variations print context instance down to the inner
>> level functions?
>
> I think you've contradicted yourself. You say the first function
> doesn't do any printing, but it calls other functions that do. Yet
> viewed in terms of its contract, either this first function is
> supposed to print something or it isn't. If it is, the fact that it
> does so only indirectly, by calling other functions, is irrelevant --
> it still has printing as part of its contract, and therefore I don't
> have a problem with making it pass around a printer context.

J'ever think that a function might be used in a context not originally
envisioned by it's original author? I thought not.

-russ

Ken Tilton

unread,
Aug 26, 2007, 2:00:45 PM8/26/07
to

Actually we are trying to decide if the OPs use case is ordinary or not,
so the last sentence tells us nothing.

OK, now what happens if we listen to you and the state is passed as
parameters along with other params. Now we have to do some
refactoringtand state manipulation logic is moving one level deeper in
to a subroutine, such that the original function no longer touches it?
It just does some worjk and then calls one or another state-manipulating
function to handle that task. The original sadly still needs the state
parameter purely to pass it along to the new state-manipulating subroutines.

Having a parameter just so you can pass it to someone else is one way
God tells you to use a special.

Matthias Buelow

unread,
Aug 26, 2007, 6:50:42 PM8/26/07
to
Ken Tilton <kenny...@optonline.net> wrote:

> meanwhile, please explain how structured programming is a slippery slope
> inevitably leading to unmaintainable code.

Anything eventually leads to unmaintainable code, the best you can do is
defer the inevitable horror a little bit. That's why software always
gets reinvented. Only dead programs never get replaced or rewritten.

> And why am I wasting time on a nincompoop?

No clue.. just keep on reading c.l.l... Keeps you away from the
nincompoops.

Ken Tilton

unread,
Aug 26, 2007, 8:27:17 PM8/26/07
to

Matthias Buelow wrote:
> Ken Tilton <kenny...@optonline.net> wrote:
>
>
>>I meant to ask, when /should/ they be used?
>
>
> There are some exceptional situations when they come in handy, don't
> they.

No, there is nothing exceptional about it. The OP has a pile of state
and a call tree. He knows, I know, and you know that the whole call tree
is operating on the same pile of state. How can we best make that
evident? Certainly not by creating non-parameterizing parameters to
functions just to worship your God of Functional Purism. That in fact
misleads readers to thinking that anyone anywhere can call the function
with a different pile of state. Nonsense. That call tree is understood
to work together on precisely one pile of state. No confusion arises,
only clarity. Gosh, what is this thing I am working on? Oh *golly*, it's
a special. Let's see the one fricking place it is bound. Holy crap,
there are two. OK, in the middle of X we need to recursively invoke this
subsystem on a different pile of state. Coolio.

You are a failed Lisper, and belong in the camp of Thou shalt have no
god before me. You see specials, shreak in horror, and desperately
demand they be confined, quarantined, constrained, and constricted to
PRINT. Missing the whole point of Lisp. We have tools, lots of tools,
lots of different kinds of tools, and lotsa paradigms. We do not what we
fricking well please and write great code with it without a problem

You want there to be a problem, just like the strong static typers want
there to be a problem. Unfortunately for all you finger shaking, rule
making, strait jacket wearing school marms we have a nonexistence proof
of craploads of great code being written without a problem in spite of
your sky is falling obsessive compulsive gnashing of the teeth.

Alex Mizrahi

unread,
Aug 26, 2007, 8:57:04 PM8/26/07
to
(message (Hello 'Ken)
(you :wrote :on '(Fri, 24 Aug 2007 15:25:45 -0400))
(

??>> You can pass the state as argument(s) (if practical) for a more
??>> functional approach... that's what I like doing although many here are
??>> really into setq/setf.

KT> The use case described by the OP is a good one for special variables.

i agree with this -- most likely special variables will make most succinct,
no-bullshit solution.
as Paul Graham wrote, succinctness is power :).

certainly there are some drawbacks like "namespace pollution" and "causing
unpleasant interferences", but if that's not some part of large project,
such drawbacks won't have any effect, and avoiding special variables in such
cases are just kinda superstitions

)
(With-best-regards '(Alex Mizrahi) :aka 'killer_storm)
"choose no life")


Mark Cox

unread,
Aug 27, 2007, 10:55:54 AM8/27/07
to
I happen to agree with Ken on this one, but I can understand the
concern the others have with not specifying all arguments.

Why couldn't you create a macro like:

(requires-specials (:vars ((*foo* (make-instance 'context)))
:parameters (*bar*)
:others (*a-special-already-defined-elsewhere*)
:append-doc-string "Requires special ~A")

(defun myfunc (something) ...)
(defun anotherfunc (something-else) ...)

;; I tend to assume that the specials are read only once you enter
;; the function tree, but I guess you could do something like this
;; aswell if you're really thorough.
(modifies-special *bar*
(defun something-that-modifies-*bar* (val) ...)))

Atleast this way you can tell from the indent that the functions are defined
inside something, and when you're not looking at the source,
the doc string for each function is modified. You could
also make requires-specials auto modify the doc string for each
special aswell by appending the functions that it has an effect on.

Mark

Dimiter "malkia" Stanev

unread,
Aug 28, 2007, 9:45:15 PM8/28/07
to
Special (dynamic) variables are one of the things that got me into Lisp.

I grew tired of supporting an in-house tool (C++), which had to be
extended with various options, and I've ended up making each function
taking (..., int argc, char *argv[]) and then each function would parse
it (a bit of greenspunning).

Then I've put the stuff in global variables, and removed the (..., int
argc, char *argv[]), but for some reason I needed to change some of the
things temporarily, and restore them, so I've ended up doing something
like a stack for each global variable.

And when I started reading lisp I understand the wisdom behind special
variables, and all my wasted hours behind emulating what's already have
been done.

That plus the multiple-value returns (Another invention that I needed
these days in a C++ code to overcome total rewrite of foreign code
portions in a much larger tool - mainly to avoid massive amounts of
DLL's to be recompiled).

Dimiter "malkia" Stanev

unread,
Aug 28, 2007, 10:00:46 PM8/28/07
to
> Anything eventually leads to unmaintainable code, the best you can do is
> defer the inevitable horror a little bit. That's why software always
> gets reinvented. Only dead programs never get replaced or rewritten.

Yes software gets rewritten, but most of the time it just evolves (good
or bad). The question is how easy are the tools to make the evolution
easier. My daily job is C++, and everytime I add or change a virtual
member function I have to recompile a bunch of .DLL's (or static .libs),
or even if just add an optional argument, I have to do the same. Andnd
sometimes I can't really evolove or change, bent the program behaviour,
unless I rewrite whole portions of the code. At the same time, when I'm
ready with the change, some other programmer might've already changed
something and submitted to P4 before my stuff get's in. Most of the time
I'm dealing with that.

I believe dynamic non-strict language like Common Lisp, Python, others
would help evolving easier (even if that's not always creating correct
code). I much rather prefer faster evolution, mutation, prototyping,
called it what you want, than revolution, where you have to start either
from scratch, or rewrite massive amounts of code to get your new feature.

I also like my P4 changelists to be really small as diffs.

Ken Tilton

unread,
Aug 28, 2007, 11:46:03 PM8/28/07
to

Dimiter "malkia" Stanev wrote:
> Special (dynamic) variables are one of the things that got me into Lisp.

OK, but you are only allowed by the finger shakers to use them for PRINT.

>
> I grew tired of supporting an in-house tool (C++), which had to be
> extended with various options, and I've ended up making each function
> taking (..., int argc, char *argv[]) and then each function would parse
> it (a bit of greenspunning).

It occurs to me that the infinitive should be "to greenspin". Certainly
"So there I am using the C preprocessor to greenspin macros and I hit a
wall..." is easier to say.

>
> Then I've put the stuff in global variables, and removed the (..., int
> argc, char *argv[]), but for some reason I needed to change some of the
> things temporarily, and restore them, so I've ended up doing something
> like a stack for each global variable.
>
> And when I started reading lisp I understand the wisdom behind special
> variables, and all my wasted hours behind emulating what's already have
> been done.
>
> That plus the multiple-value returns (Another invention that I needed
> these days in a C++ code to overcome total rewrite of foreign code
> portions in a much larger tool - mainly to avoid massive amounts of
> DLL's to be recompiled).

You need to read Burdick's Road to Lisp. Then add your own. I think he
was doing macros in C++.

OK, this NG has become unbearable with the stench of nooby diapers. What
is the obscure niche language where I will be safe forever? I am
thinking Forth. Yes? No?

kxo

Rob Warnock

unread,
Aug 29, 2007, 4:20:56 AM8/29/07
to
Ken Tilton <kent...@gmail.com> wrote:
+---------------
| Dimiter "malkia" Stanev wrote:
| > ...various options, and I've ended up making each function
| > taking (..., int argc, char *argv[]) and then each function would parse
| > it (a bit of greenspunning).
|
| It occurs to me that the infinitive should be "to greenspin". Certainly
| "So there I am using the C preprocessor to greenspin macros and I hit a
| wall..." is easier to say.
+---------------

Nah, "to greenspin" is what Ben Bernanke's doing about mortgage rates...
[Hint: Whom did he succeed?]

Daniel Barlow

unread,
Aug 29, 2007, 8:06:01 AM8/29/07
to
Rob Warnock wrote:
>> For me, the two classic use cases [for specials] are:

>
> 1. Things that work more-or-less like the ANSI-defined specials
> such as *READ-BASE*, that is, large numbers of miscellaneous
> global parameters that you *usually* want to just leave alone
> but sometimes want to rebind for the dynamic extent of one
> subtree of function calls.
>
> Thanks *goodness* for specials for this case!! Passing the ~20 (!)
> or so specials that affect the Lisp printer [see CLHS 22.1.1.1]
> around in every function call just to enforce some kind of
> functional "purity" would simply be insane.

It's interesting you should use this as an example, because I've always
thought the reader/printer variables would be much better suited as
per-stream attributes of some kind

(setf (stream-read-base myfile) 16)

than applying to every stream in some dynamic contour. Otherwise you
get really strange behaviour when you do

(let ((*print-base* 13)) (some-computation))

and some-computation blows up landing you in the debugger. Or if you
don't get really strange behaviour it's only because the implementor has
been to some lengths to have the debugger rebind everything again.


-dan

David Combs

unread,
Sep 22, 2007, 4:49:07 PM9/22/07
to
In article <9Y5Bi.76$IM6...@newsfe12.lga>,
Ken Tilton <kent...@gmail.com> wrote:
>
>
...

>You need to read Burdick's Road to Lisp. ...

...

THANK YOU, KEN!!!!

I googled Burdick "road to lisp"

and got what I think was the entire survey, responses, etc. INCREDIBLE!

How could I not already have known about this?


(Maybe someday someone could massage it into one large
tekinfo or pdf or whatever, for people to print out
and read at leisure (train, "throne room", bed, etc)?

And tell lisp-questioners and berators(sp?) to study it,
and *then* come back. )


Thanks again, ken.

David (lurker)


Ken Tilton

unread,
Sep 22, 2007, 11:18:49 PM9/22/07
to

David Combs wrote:
> In article <9Y5Bi.76$IM6...@newsfe12.lga>,
> Ken Tilton <kent...@gmail.com> wrote:
>
>>
> ...
>
>
>>You need to read Burdick's Road to Lisp. ...
>
>
> ...
>
> THANK YOU, KEN!!!!
>
> I googled Burdick "road to lisp"
>
> and got what I think was the entire survey, responses, etc. INCREDIBLE!
>
> How could I not already have known about this?

The Yobbos decided to punish me by forcing the survey off Cliki on the
grounds that it was a commercial product.

Also, no link, no traffic:

http://wiki.alu.org/The_Road_to_Lisp_Survey

I should check, see if we have any new respondents. You found the
highlight film, right?

http://wiki.alu.org/RtL_Highlight_Film

That is always fun to read.

kenny

Brian Adkins

unread,
Sep 23, 2007, 1:49:48 AM9/23/07
to
On Sep 22, 11:18 pm, Ken Tilton <kennytil...@optonline.net> wrote:
> The Yobbos decided to punish me by forcing the survey off Cliki on the
> grounds that it was a commercial product.

What's commercial about the survey?

Ken Tilton

unread,
Sep 23, 2007, 8:17:34 AM9/23/07
to

Sorry, I collapsed a double-negative. The real reason was that some of
the people who responded do not produce non-commercial software... Hmmm,
that still sounds stupid.

To be fair the survey was quite popular in the community so they needed
a respectable reason and I guess they felt "We hate Kenny" would not do.

Ironically their character assassination campaign has succeeded so well
that they would look less silly now had they gone with "Omigod! We
killed Kenny's survey!", but hindsight is always a 20/20 shotgun.

kenny

John Thingstad

unread,
Sep 23, 2007, 12:25:52 PM9/23/07
to
På Sun, 23 Sep 2007 14:17:34 +0200, skrev Ken Tilton
<kenny...@optonline.net>:

> Ironically their character assassination campaign has succeeded so well
> that they would look less silly now had they gone with "Omigod! We
> killed Kenny's survey!", but hindsight is always a 20/20 shotgun.
>
> kenny

That exclamation is property of "South Park" and thus a copyright
violation. ;)

d...@telent.net

unread,
Oct 1, 2007, 4:24:39 PM10/1/07
to
Ken Tilton wrote:
> Brian Adkins wrote:
>> On Sep 22, 11:18 pm, Ken Tilton <kennytil...@optonline.net> wrote:
>>
>>> The Yobbos decided to punish me by forcing the survey off Cliki on the
>>> grounds that it was a commercial product.
>>
>>
>> What's commercial about the survey?
>>
>
> Sorry, I collapsed a double-negative. The real reason was that some of
> the people who responded do not produce non-commercial software... Hmmm,
> that still sounds stupid.

No, the real reason was that Kenny asked to have it transferred. See
http://groups.google.co.uk/group/comp.lang.lisp/msg/54c9edc3bfab90f2 and
followups


-dan

Ken Tilton

unread,
Oct 1, 2007, 5:11:00 PM10/1/07
to

d...@telent.net wrote:
> Ken Tilton wrote:
>
>> Brian Adkins wrote:
>>
>>> On Sep 22, 11:18 pm, Ken Tilton <kennytil...@optonline.net> wrote:
>>>
>>>> The Yobbos decided to punish me by forcing the survey off Cliki on the
>>>> grounds that it was a commercial product.
>>>
>>>
>>>
>>> What's commercial about the survey?
>>>
>>
>> Sorry, I collapsed a double-negative. The real reason was that some of
>> the people who responded do not produce non-commercial software...
>> Hmmm, that still sounds stupid.
>
>
> No, the real reason was that Kenny asked to have it transferred.

Chya. Your evidence begins with someone suggesting an adoptive home
after you dropped the RtL off at Bradshaw's for melting down? Why
couldn't you have defended OJ?

If anyone wonders what we are talking about, what is left of the exiled
affront to Clikidom can be found here in an ALU refugee camp:

http://wiki.alu.org/RtL_Highlight_Film

Well, that's the highlight film. Click on each author to read the full
story from which the highlight was extracted.

I have not hawked the thing in years, but the past six months have seen
an awful lot of noobs, and it is still interesting how folks are finding
Lisp. It will stop being interesting when O'Reilly starts soliciting
Lisp titles.

kenny

ps. Maybe lemme know if you do one, here or by email. kt

d...@telent.net

unread,
Oct 1, 2007, 5:41:49 PM10/1/07
to
Ken Tilton wrote:
> Chya. Your evidence begins with someone suggesting an adoptive home
> after you dropped the RtL off at Bradshaw's for melting down? Why
> couldn't you have defended OJ?

Bradshaw's a yobbo now? Either I missed something important while I was
away, or the mind-control helicopters found something in my head to work
on after all, cos I totally don't remember that.

> ps. Maybe lemme know if you do one, here or by email. kt

Do a RtL, or do an O'Reilly title?

http://wiki.alu.org/Daniel_Barlow's_Road_to_Lisp ; 2004-10-31
http://lemonodor.com/archives/000875.html ; see third comment

-dan

Ken Tilton

unread,
Oct 1, 2007, 5:55:27 PM10/1/07
to

d...@telent.net wrote:
> Ken Tilton wrote:
>
>> Chya. Your evidence begins with someone suggesting an adoptive home
>> after you dropped the RtL off at Bradshaw's for melting down? Why
>> couldn't you have defended OJ?
>
>
> Bradshaw's a yobbo now? Either I missed something important while I was
> away, or the mind-control helicopters found something in my head to work
> on after all, cos I totally don't remember that.

All you have to do is ask around at Cliki HQ and find out who ordered
the eviction of The Road to Lisp because (true) the Cliki charter is "of
or pertaining to free Lisp software". Your non-countering
counter-evidence picks up the story with the RtL sitting in a shelter
waiting to be euthanized and someone suggesting an adoptive home.

>
>> ps. Maybe lemme know if you do one, here or by email. kt
>
>
> Do a RtL, or do an O'Reilly title?

I was addressing the diaper-clad amongst us.

I wonder if Peter saw an uptick this summer, I am doing my best to drive
these rugrat pests out of here so they have to buy his book instead.

0 new messages