Pass by reference

47 views
Skip to first unread message

Greg Menke

unread,
Nov 13, 2000, 3:00:00 AM11/13/00
to

Hi,

I have a defun thats getting pretty large, and would like to split it
up. The problem is I have a bunch of local let variables which I'd
like to pass into subordinate functions, where they are modified and
retain the modified values upon return. I would rather avoid creating
global variables, so am looking for a reasonable method for passing
them by reference. The variables consist of a couple lists, a char or
two and some numerics & strings.

Or, if there is a preferred way to deal with this kind of situation,
that would be fine too. A redesign of the program architecture,
allowing an alternative approach would also be fine.

I get the feeling I'm missing something obvious about how variables
want to operate in Lisp. I apologize if this is an ill-considered
question, all hints are cheerfully accepted.

Thanks,

Gregm


Thomas A. Russ

unread,
Nov 13, 2000, 3:00:00 AM11/13/00
to
Greg Menke <gregm-n...@zxy.mindspring.com> writes:

> Hi,
>
> I have a defun thats getting pretty large, and would like to split it
> up. The problem is I have a bunch of local let variables which I'd
> like to pass into subordinate functions, where they are modified and
> retain the modified values upon return.

Lisp doesn't let you do this. All values are passed by reference, but
the standard languages doesn't have locatives (pointers to variable
value cells), so you can't modify such things.

> I would rather avoid creating
> global variables, so am looking for a reasonable method for passing
> them by reference. The variables consist of a couple lists, a char or
> two and some numerics & strings.

One standard mechanism for doing this in Lisp would be to pass the
values normally as arguments to the subfunction and have the subfunction
return multiple values. You then use multiple-value-setq in the caller
to affect the variable value changes.

Example:

(defun first ()
(let ((x 1)
(y "one"))
(multiple-value-setq (x y) (second x y))
(print x)
(print y)))


(defun second (x y)
(values (+ x x)
(concatenate 'string y y)))

Another way to do this would be to use FLET inside the LET block and
define an internal function which gets to access the lexical environment
established by the LET block. This might not help you as much if the
reason for splitting is getting manageable code size, though.

Example:

(defun first ()
(let ((x 1)
(y "one"))
(flet ((second ()
(setq x (+ x x))
(setq y (concatenate 'string y y))))
(second)
(print x)
(print y))))

Finally you could always use special variables which do not actually
have to be global, although this is usually not a preferred solution
unless you need to pass information across many levels of function call
nesting:

(defun first ()
(let ((*x* 1)
(*y* "one"))
(declare (special *x* *y*))
(second)
(print *x*)
(print *y*))))

(defun (second ()
(declare (special *x* *y*))
(setq x (+ *x* *x*))
(setq y (concatenate 'string *y* *y*))))

Calling (second) at top level will generate unbound variable errors.


--
Thomas A. Russ, USC/Information Sciences Institute t...@isi.edu

Tim Bradshaw

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
Greg Menke <gregm-n...@zxy.mindspring.com> writes:

> Hi,
>
> I have a defun thats getting pretty large, and would like to split it
> up. The problem is I have a bunch of local let variables which I'd
> like to pass into subordinate functions, where they are modified and

> retain the modified values upon return. I would rather avoid creating


> global variables, so am looking for a reasonable method for passing
> them by reference. The variables consist of a couple lists, a char or
> two and some numerics & strings.
>

I think there are two ways to do this: pass the variables as arguments
and return the new values as return values, and then do:

(multiple-value-setq (x y z) (f x y z))

Alternatively, what I do is to have local functions created with FLET
or LABELS which can update the environment, but which give you some
kind of decomposition into functions. Of course the overall function
definition is still large, but now it's just a shell with lots of
little internal subfunctions.

--tim

Pierre R. Mai

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
Greg Menke <gregm-n...@zxy.mindspring.com> writes:

> I have a defun thats getting pretty large, and would like to split it
> up. The problem is I have a bunch of local let variables which I'd
> like to pass into subordinate functions, where they are modified and
> retain the modified values upon return. I would rather avoid creating
> global variables, so am looking for a reasonable method for passing
> them by reference. The variables consist of a couple lists, a char or
> two and some numerics & strings.
>

> Or, if there is a preferred way to deal with this kind of situation,
> that would be fine too. A redesign of the program architecture,
> allowing an alternative approach would also be fine.

Others have already given several approaches that can be used in your
situation.

Depending on the nature of the state that is contained in your
variables, it might be more natural to actually put all or parts of
this state into one object (e.g. a struct or class), and pass that
around instead. E.g.

(defstruct world-state
population-count
forest-area
crop-area
...)

(defun calculate-future-world (...)
(let ((world-state (make-world-state :population-count ...)))
(loop until (zerop (world-state-population-count world-state))
do
(calculate-areas world-state)
(calculate-population world-state)
...)
world-state))

(defun calculate-population (state)
(setf (world-state-population-count state)
...))

...

Whether something like this approach is actually useful will depend on
whether the state variables are truly related or only accidentally
related.

Regs, Pierre.

--
Pierre R. Mai <pm...@acm.org> http://www.pmsf.de/pmai/
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents. -- Nathaniel Borenstein

Kenny Tilton

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
> I would rather avoid creating
> global variables, so am looking for a reasonable method for passing
> them by reference. The variables consist of a couple lists, a char or
> two and some numerics & strings.

The immoral equivalent of pass by reference: in Lisp would be:

(defun my* (x) (car x))
(defun (setf my*) (newContent x*)
(rplaca x* newContent))

(defun main ()
(flet ((beat-on (x*)
(setf (my* x*) (not (my* x*)))))
(let* ((a* (cons nil nil))) ;; use cons cell as wrapper for real
content
(dotimes (x 3)
(beat-on a*)
(format t "~&It's ~a! Wait..." (if (my* a*) "Bore" "Gush"))))))

(main)

But you correctly wish not to use globals, and in a smaller sense that is
all you are doing here: the driver function and smaller functions you have
in mind will be operating on variables "global" to the little universe
they constitute.

> Or, if there is a preferred way to deal with this kind of situation,
> that would be fine too.

Just a little better, create a struct or CLOS class with slots for each
variable. More perilously, make a list of them and use
destructuring-bind to parse it, rplaca on any given cell in the list to
destructively list contents. If what you are talking about is, say, a
control block you can identify as a bona fide application structure on
which all your code beats, this is not too bad. Does all this state
consitute some aggregate "thingy" to which you can give a name?

> A redesign of the program architecture,
> allowing an alternative approach would also be fine.

Now we're talking. There is not much point in breaking up a large function
in the manner you propose; it is almost better to have everything in one
chunk since i still need to see everything to understand it. But if you
look carefully you can probably isolate chunks of code which make a single
determination using only one or two of the many bits of state you have
floating around...break those out as functions with one or two parameters
(so you can feed in the state) and then do something in the driver
function with the result.

Years ago I took over a project from someone, and was all excited to find
it consisted of many tiny little (VAX basic) functions. Pretty soon I
realized that those functions all read and modified global state. One
letter of the structured programming approach had been honored (small
functions) but not the rule: the small functions should be black boxes,
hiding their implementation. Sounds like you are likewise thinking
"smaller pieces" without thinking "hiding implementation details"...try to
truly decompose the large function into smaller functions, not smaller
pieces of code.

kenny


Gary Curtis

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
Lisp does not support call-by-reference, so you would generally accomplish
what you want by explicitly returning the modified value. For example:

(defun foo ()
(let ((a 1) (b "foo") (c (list 'a 'b 'c)))
(setf a (do-something-to a))
(setf b (do-something-to b))
(setf c (do-something-to c))
(format t "~A ~A ~A" a b c))))

uses three local variables. Each is manipulated in some way in the
body of the function. This can be moved to a separate function by:

(defun foo2 ()
(let ((a 1) (b "foo") (c (list 'a 'b 'c)))
(multiple-value-setq (a b c)
(foo-aux a b c))
(format t "~A ~A ~A" a b c))

(defun foo-aux (a b c)
(setf a (do-something-to a))
(setf b (do-something-to b))
(setf c (do-something-to c))
(values a b c))

The use of values & multiple-value-setq allow multiple values to
be returned by a function which it sounds like you may need.

Gary.
--

In article <m3n1f3m...@mindspring.com>, Greg Menke <gregm-n...@zxy.mindspring.com> writes:
|>
|> Hi,
|>

|> I have a defun thats getting pretty large, and would like to split it
|> up. The problem is I have a bunch of local let variables which I'd
|> like to pass into subordinate functions, where they are modified and

|> retain the modified values upon return. I would rather avoid creating


|> global variables, so am looking for a reasonable method for passing
|> them by reference. The variables consist of a couple lists, a char or
|> two and some numerics & strings.
|>

|> Or, if there is a preferred way to deal with this kind of situation,

|> that would be fine too. A redesign of the program architecture,


|> allowing an alternative approach would also be fine.
|>

Geoff Summerhayes

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
I'm a newbie, but would be possible to enclose the function and subfunctions
with a let, sort of local globals?

Geoff

"Greg Menke" <gregm-n...@zxy.mindspring.com> wrote in message
news:m3n1f3m...@mindspring.com...

Hallvard B Furuseth

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
t...@sevak.isi.edu (Thomas A. Russ) writes:

> Another way to do this would be to use FLET inside the LET block and
> define an internal function which gets to access the lexical environment

> established by the LET block. (...)


> (defun first ()
> (let ((x 1)
> (y "one"))
> (flet ((second ()
> (setq x (+ x x))
> (setq y (concatenate 'string y y))))
> (second)


(third #'second)

might be more relevant here.

> (print x)
> (print y))))


Of course you _could_ implement call-by-reference by hand, something
like the code below.
(Actually this does call by _name_, i.e. the argument is re-evaluated
each time it is referenced. (named-arg PLACE) would have to examine
PLACE a bit more carefully to create call by reference.)

;;;; Implement call by name (somewhat similar to call by reference)

;;; (place-name PLACE) returns a closure C for which:
;;; (funcall C) returns the result of evaluating PLACE, and
;;; (funcall C new-val) returns the result of (setf PLACE new-val).
;;; The PLACE form is re-evaluated at each call.
;;;
;;; (capture-place-names (VAR...) BODY)
;;; where each VAR is bound to a place-name closure,
;;; lexically binds each VAR to a symbol macro accessing its PLACE.
;;; Thus, BODY can say (incf VAR) for (funcall VAR (1+ (funcall VAR))).
;;;
;;; Example:
;;; (defun foo (x y z)
;;; (capture-place-names (x y)
;;; (setf x (* y z))))
;;;
;;; (let ((list (list 3)) (val 5))
;;; (foo (place-name (car list)) (place-name val) 7)
;;; list)
;;; ==> (35)

(defconstant *place-name-default* (gensym))

(defmacro place-name (place)
(if (let ((tmp (macroexpand place)))
;; This should test is (setf TMP) is OK, if I remembered how.
(or (constantp tmp)
(not (or (symbolp tmp)
(consp tmp)))))
`(lambda () ,place)
`(lambda (&optional (new-val *place-name-default*))
(if (eq new-val *place-name-default*)
,place
(setf ,place new-val)))))

;; For lazy users...
(defmacro capture-place-names (vars &rest body)
(unless (listp vars)
(setq vars (list vars)))
(let ((funcs (mapcar #'(lambda (x)
(declare (ignore x))
(gensym))
vars)))
`(flet ,(mapcan (lambda (func var)
`((,func () (funcall ,var))
((setf ,func) (new-val) (funcall ,var new-val))))
funcs vars)
(declare (ignorable ,@(mapcan #'(lambda (func) `(#',func
;; (setf ,func)
))
funcs)))
(symbol-macrolet ,(mapcar (lambda (func var)
`(,var (,func)))
funcs vars)
(declare (ignorable ,@vars))
,@body))))


Next step, would be to define a macro defun-named-args, so
(defun-named fun (arg1 (:named arg2) arg3 ...) ...)
(fun v1 v2 v3)
would expand to
(defmacro fun (...) ... fun-wrapper ...)
(defun fun-wrapper (arg1 arg2 arg3 ...)
(capture-place-names (arg2)
...))
(fun-wrapper v1 (named-arg v2) v3)
at which point you have probably done a lot more work than
a solution with simple closures or a struct or something:-)

--
Hallvard

Rainer Joswig

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
In article <HBF.2000...@bombur.uio.no>, Hallvard B Furuseth
<h.b.fu...@usit.uio.no> wrote:

> t...@sevak.isi.edu (Thomas A. Russ) writes:
>
> > Another way to do this would be to use FLET inside the LET block and
> > define an internal function which gets to access the lexical environment
> > established by the LET block. (...)
> > (defun first ()
> > (let ((x 1)
> > (y "one"))
> > (flet ((second ()
> > (setq x (+ x x))
> > (setq y (concatenate 'string y y))))
> > (second)
>
>
> (third #'second)

How about:

(defun foo (n)
(let ((a n)
(b (1+ n)))
(foo-1)
(list a b)))

(defun foo-1 ()
(setf a 13))

; does not really work
(print (foo 3))

; let's try again
(defun foo (n)
(let ((a n)
(b (1+ n)))
(declare (special a b))
(foo-1)
(list a b)))

; oops
(print (foo 3))

--
Rainer Joswig, Hamburg, Germany
Email: mailto:jos...@corporate-world.lisp.de
Web: http://corporate-world.lisp.de/

Hallvard B Furuseth

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to
I wrote:
> (Actually this does call by _name_, i.e. the argument is re-evaluated
> each time it is referenced. (named-arg PLACE) would have to examine
> PLACE a bit more carefully to create call by reference.)

Oops. Replace `named-arg' with `place-name' through this article.


Rainer Joswig <jos...@corporate-world.lisp.de> wrote:
>
> How about:
>
> (...)


> (defun foo (n)
> (let ((a n)
> (b (1+ n)))
> (declare (special a b))

The original post wanted to avoid global variables.

--
Hallvard

Greg Menke

unread,
Nov 14, 2000, 3:00:00 AM11/14/00
to

>
> > A redesign of the program architecture,
> > allowing an alternative approach would also be fine.
>
> Now we're talking. There is not much point in breaking up a large function
> in the manner you propose; it is almost better to have everything in one
> chunk since i still need to see everything to understand it. But if you
> look carefully you can probably isolate chunks of code which make a single
> determination using only one or two of the many bits of state you have
> floating around...break those out as functions with one or two parameters
> (so you can feed in the state) and then do something in the driver
> function with the result.


The defun in question is getting too big, and is starting to have
tendencies towards duplication in a couple places, so subordinate
functions are suggested. The defun has a fair bit of internal state,
but the variables are all centered around the function of the defun
itself, and could reasonably be combined, so I think the struct
approach is what I'll use.

According to my reading of the posts on this thread, the contents of
an instance of a struct are not copied when it is passed into a defun.
Is it correct to say the binding to the struct seen by the caller is
different from the binding seen by the subroutine, but both bindings
point to a single instance of the struct?

Can I conclude that all atoms, strings and chars are passed by value
in the default case? What about vectors, arrays, hashes, and the
other complex datatypes, aside from structs?

Thanks everyone for your responses,

Gregm

Robert Monfera

unread,
Nov 14, 2000, 9:48:50 PM11/14/00
to
"Thomas A. Russ" wrote:

> Another way to do this would be to use FLET inside the LET block and
> define an internal function which gets to access the lexical
> environment

> established by the LET block. This might not help you as much if the
> reason for splitting is getting manageable code size, though.
>
> Example:
>

> (defun first ()
> (let ((x 1)
> (y "one"))
> (flet ((second ()
> (setq x (+ x x))
> (setq y (concatenate 'string y y))))
> (second)

> (print x)
> (print y))))

Or one can even nest the defuns inside lets and flets this way:

(let ((x 1)
(y "one"))

(flet ((seconnd ()


(setq x (+ x x))
(setq y (concatenate 'string y y))))

(defun firrst ()
(second)
(print x)
(print y))
(defun thirrd ()
(second)
(print y)
(print x))))

The benefit is that several functions may have access to lexical
bindings without polluting the global name spaces with seconnd, x and y.

Robert

Tim Bradshaw

unread,
Nov 15, 2000, 1:27:35 AM11/15/00
to
* Greg Menke wrote:

> Can I conclude that all atoms, strings and chars are passed by value
> in the default case? What about vectors, arrays, hashes, and the
> other complex datatypes, aside from structs?

Lisp passes everything by value. Actually there can be small
exceptions for things like numbers & characters, but since these are
non-mutable objects this doesn't often matter.

--tim

Kent M Pitman

unread,
Nov 15, 2000, 2:55:52 AM11/15/00
to
Tim Bradshaw <t...@cley.com> writes:

> * Greg Menke wrote:
>
> > Can I conclude that all atoms, strings and chars are passed by value
> > in the default case? What about vectors, arrays, hashes, and the
> > other complex datatypes, aside from structs?
>
> Lisp passes everything by value.

What Tim says is true, but the problem is that "by value" is not a well-defined
term. It means something else in other languages.

The best way to say it, I think, is that Lisp passes objects "by identity".
(It would be called "by pointer" in other languages, but for the fact that
Lisp has no objects which are not pointers. Every object is a pointer to an
object, and so we just never use the word pointer except redundantly for
emphasis. 3 is a pointer to 3. a hash table is a pointer to a hash table.)

> Actually there can be small
> exceptions for things like numbers & characters, but since these are
> non-mutable objects this doesn't often matter.

Objects are never copied when passed as arguments with the single exception
that a few primitive datatypes are permitted to lose their identity (numbers
and characters, in particular) because they are really never supposed to be
referred to by identity in the first place. If you always use EQL and never
use EQ, and you think of EQL as the primitive identity-teseting operation,
ignoring EQ altogether, you will never see this subtle effect I'm writing
about in this paragraph. (i.e., it's not just the lack of mutators that
would let you notice this, as Tim observes, but also EQ, because its job in
life is to occasionally and mostly uselessly observe the fiction we sometimes
tell about all objects being passed by identity).

(Incidentally, strings and chars are atoms. By atoms, Greg probably means
symbols. He should learn to use the correct terminology. Only conses are
non-atoms. Atom means literally non-cons and is consequently a nearly
useless term in Lisp, kept mostly for historical reasons. Hash tables,
vectors, arrays and structs are all atoms, for better or worse.)

Fernando Rodríguez

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
On Wed, 15 Nov 2000 07:55:52 GMT, Kent M Pitman <pit...@world.std.com> wrote:

>Objects are never copied when passed as arguments with the single exception
>that a few primitive datatypes are permitted to lose their identity (numbers

What about lists? I thought that when a list a was passed to a
function f, f received a pointer to the equivalent of (copy-list a), instead
of a pointer to a... :-?


//-----------------------------------------------
// Fernando Rodriguez Romero
//
// frr at mindless dot com
//------------------------------------------------

Kent M Pitman

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
Fernando Rodríguez <spa...@must.die> writes:

> On Wed, 15 Nov 2000 07:55:52 GMT, Kent M Pitman <pit...@world.std.com> wrote:
>
>
>

> >Objects are never copied when passed as arguments with the single exception
> >that a few primitive datatypes are permitted to lose their identity (numbers
>

> What about lists? I thought that when a list a was passed to a
> function f, f received a pointer to the equivalent of (copy-list a), instead
> of a pointer to a... :-?

Nope.

(DEFVAR *FOO* (LIST 'A 'B 'C))

(DEFUN FOO (X Y) (SETF (CAR X) Y))

(FOO *FOO* 'Z)

*FOO* => (Z B C)


Greg Menke

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to

> Objects are never copied when passed as arguments with the single exception
> that a few primitive datatypes are permitted to lose their identity (numbers
> and characters, in particular) because they are really never supposed to be
> referred to by identity in the first place. If you always use EQL and never
> use EQ, and you think of EQL as the primitive identity-teseting operation,
> ignoring EQ altogether, you will never see this subtle effect I'm writing
> about in this paragraph. (i.e., it's not just the lack of mutators that
> would let you notice this, as Tim observes, but also EQ, because its job in
> life is to occasionally and mostly uselessly observe the fiction we sometimes
> tell about all objects being passed by identity).
>
> (Incidentally, strings and chars are atoms. By atoms, Greg probably means
> symbols. He should learn to use the correct terminology. Only conses are
> non-atoms. Atom means literally non-cons and is consequently a nearly
> useless term in Lisp, kept mostly for historical reasons. Hash tables,
> vectors, arrays and structs are all atoms, for better or worse.)

I was using atom in the CLtL2 sense, generally meaning non-cons
objects- though I wasn't completely clear on what that covers. Since
the definition there didn't explicitly deal with the more complex
types, I assumed a restricted definition on atomic which covered
numbers, chars and strings.

Thanks for clearing this up for me.

Gregm

cbbr...@hex.net

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
Greg Menke <gregm-n...@zxy.mindspring.com> writes:
> I have a defun thats getting pretty large, and would like to split it
> up. The problem is I have a bunch of local let variables which I'd
> like to pass into subordinate functions, where they are modified and
> retain the modified values upon return. I would rather avoid creating
> global variables, so am looking for a reasonable method for passing
> them by reference. The variables consist of a couple lists, a char or
> two and some numerics & strings.
>
> Or, if there is a preferred way to deal with this kind of situation,
> that would be fine too. A redesign of the program architecture,

> allowing an alternative approach would also be fine.
>
> I get the feeling I'm missing something obvious about how variables
> want to operate in Lisp. I apologize if this is an ill-considered
> question, all hints are cheerfully accepted.

Others have suggested various ideas about embedding the namespace
either:

a) Locally into the function, or
b) Locally into a Structure.

Let me suggest two more options:

c) [I'll be vague here :-(.] You might organize the data using CLOS
via slots in a class.

In effect, this amounts to a more sophisticated (and hard to define)
variation of using DEFSTRUCT. If the data you're working with can be
coherently treated as "a structure," then DEFSTRUCT is almost
certainly a simpler way to deal with it. [Flip side to this: You can
use WITH-SLOTS to diminish the need to use accessor functions to get
at the slots; there is no equivalent in the DEFSTRUCT function set...]

d) If you have quite a bunch of interconnected functions, and quite a
set of data structures, you might want to use that to define a
PACKAGE.

A package provides:
-> Its own name space; you can use DEFVAR and DEFPARAMETER to define
"global" variables that are only visible within the scope of the
package;
-> A way of exporting those names that should be visible from outside.

If your set of functions and data structures is really quite "messy,"
it seems to me that a package might be the best tool to contain that
"mess," keeping it in one file.
--
(concatenate 'string "cbbrowne" "@hex.net") <http://www.ntlug.org/~cbbrowne/>
To err is human, to moo bovine.

patric...@my-deja.com

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
Hello Tim,
that's not true. Lisp passes everything by reference. Small things
may be passed by value. You can prove this by RPLACA-ing (or by SETF(
blah blah )) some part of a list in a called defun and printing out
the argument after the call returns.

regards,
Patrick


Sent via Deja.com http://www.deja.com/
Before you buy.

Kent M Pitman

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
patric...@my-deja.com writes:

> Hello Tim,
> that's not true. Lisp passes everything by reference. Small things
> may be passed by value. You can prove this by RPLACA-ing (or by SETF(
> blah blah )) some part of a list in a called defun and printing out
> the argument after the call returns.

I partly corrected Tim for his use of a standard term in an unusual way,
but here's my chance to emphasize the other half of the issue. Tim's use
of call-by-value is correct in that we in the Lisp community do often
call what Lisp does call-by-value, we just don't mean the kind of call-by-value
that other languages use. That's why I prefer the call-by-identity name.

One reason call-by-value is used, though, is that Lisp "supports" what we
call the "delete bug". That is,
(SETQ *X* (LIST 'A 'B 'C))
(DELETE 'A *X*)
*X* => (A B C)
That is, the arg passed is *not* what is traditionally meant in
call-by-reference languages where a locative to the variable *X*
would be passed. Rather, the value of *X*, the object (A B C),
is what is received by DELETE, hence the sometimes name call-by-value.
It's the argument's value, not its reference, that is passed.

So it's reasonable for you to be concerned about the use of the term
call-by-value but you shouldn't think it is without foundation, and
you shouldn't replace it with the equally mislead term call-by-reference,
which has its problems as well.

Duane Rettig

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
Kent M Pitman <pit...@world.std.com> writes:

> Tim Bradshaw <t...@cley.com> writes:
>
> > * Greg Menke wrote:
> >
> > > Can I conclude that all atoms, strings and chars are passed by value
> > > in the default case? What about vectors, arrays, hashes, and the
> > > other complex datatypes, aside from structs?
> >
> > Lisp passes everything by value.
>
> What Tim says is true, but the problem is that "by value" is not a well-defined
> term. It means something else in other languages.
>
> The best way to say it, I think, is that Lisp passes objects "by identity".
> (It would be called "by pointer" in other languages, but for the fact that
> Lisp has no objects which are not pointers. Every object is a pointer to an
> object, and so we just never use the word pointer except redundantly for
> emphasis. 3 is a pointer to 3. a hash table is a pointer to a hash table.)

I like the term "by identity", and that describes Lisp argument passing well.
However, it is not necessarily the case that all Lisp identities are pointers.

Specifically, it is not necessarily the case that 3 is a pointer to 3. There
have been in the past some lisps where a fixnum within a certain range actually
points to storage which holds that particular value. But in many of the Common
Lisp implementations, especially those on GP hardware which use the bottom three
bits as tag bits (for a 32-bit lisp), the number 3 is simply the bit pattern
0000...01100. In other words, there is no actual _location_ which holds the
value 3; its value is instead encoded as the immediate bit pattern of the
"identity".

I use a set of terminology that makes these concepts clear to those I teach;
it is of course implementation specific, but at least three of the major
Common Lisps on GP hardware implement in a way that this terminology fits,
and perhaps more implementations do so also:

- An Allocation Unit (or AU) is the basic unit of lisp heap allocation. It is
the size of a cons cell, which is in most 32-bit lisps 8 bytes (in Allegro CL's
64-bit ports, it is 16 bytes). An AU is always allocated on even 8-byte
(16-byte) alignments, so that the address of the actual object ends in 3 (4)
bits of 0. (Note: there may be one-word storage values called locatives,
but I don't know of any GP-hardware lisps that implement these, though
I could be wrong).

- A LispObject is a contiguous section in lisp heap that makes up a non-
immediate lisp object. It is always a multiple of AUs.

- A LispVal is a word-sized value which has a tag field and an identifier
field. The tag field is usually 3 bits for a 32-bit port, and 4 bits for
a 64-bit port. The 32-bit LispVal looks like this:

iiiiiiiiiiiiiiiiiiiiiiiiiiiiittt

This allows up to 7 types to be directly identified by tag, and usually
involves an "other tag" which says that the type information is contained
in the LispObject itself.

The meaning of the identifier field depends on the tag. An "immediate"
LispVal is one where the tag implies that the identifier contains all of
the information for the lisp object. For all other LispVals, the identifier
forms the most significant portion of an address (i.e. a pointer), with
the least significant bits being implied as 0.


Quite a bit more could be said about this terminology, but the reason
why I presented this is to get to the point about fixnums not being
pointers. In at least three Common Lisp implementations, the tags 0 and
4 are both used as fixnum tags, for even and odd fixnums, respectively.
(For a 64-bit lisp, it is the tags 0 and 8). Thus the most significant
bit of the tag is the least significant bit of the fixnum's value with
the remaining portion of the identifier is used for the remaining
portion of the fixnum's value (including the sign bit). Arithmetic
manipulation is trivial on these values, though some pre- or post-
operations are sometimes needed to ensure the proper shifting.

Finally, representing fixnums in this way alleviates the need for _any_
actual storage to be allocated for these identities. Thus, the number
3 is simply a bit pattern; it is not a pointer to 3.

--
Duane Rettig Franz Inc. http://www.franz.com/ (www)
1995 University Ave Suite 275 Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253 du...@Franz.COM (internet)

Duane Rettig

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
patric...@my-deja.com writes:

> that's not true. Lisp passes everything by reference. Small things
> may be passed by value. You can prove this by RPLACA-ing (or by SETF(
> blah blah )) some part of a list in a called defun and printing out
> the argument after the call returns.

I like Kent's definition much better, because it contains no exceptions:
Lisp passes everything "by identity". When you say that Lisp passes by
reference, you have to make exceptions for those things that are passed
by value. Thus there is always confusion when the question is asked as
to whether Lisp is pass-by-value or pass-by-reference (the correct answer
is "no" :-)

Tim Bradshaw

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
patric...@my-deja.com writes:

> Hello Tim,


> that's not true. Lisp passes everything by reference. Small things
> may be passed by value. You can prove this by RPLACA-ing (or by SETF(
> blah blah )) some part of a list in a called defun and printing out
> the argument after the call returns.
>

Well, lisp passes by value but all values are pointers is what I
meant. Kent put it much better.

--tim

John Clonts

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
Kent M Pitman wrote:
>
> patric...@my-deja.com writes:
>
> > Hello Tim,
> > that's not true. Lisp passes everything by reference. Small things
> > may be passed by value. You can prove this by RPLACA-ing (or by SETF(
> > blah blah )) some part of a list in a called defun and printing out
> > the argument after the call returns.
>
> I partly corrected Tim for his use of a standard term in an unusual way,
> but here's my chance to emphasize the other half of the issue. Tim's use
> of call-by-value is correct in that we in the Lisp community do often
> call what Lisp does call-by-value, we just don't mean the kind of call-by-value
> that other languages use. That's why I prefer the call-by-identity name.
>
> One reason call-by-value is used, though, is that Lisp "supports" what we
> call the "delete bug". That is,
> (SETQ *X* (LIST 'A 'B 'C))
> (DELETE 'A *X*)
> *X* => (A B C)
> That is, the arg passed is *not* what is traditionally meant in
> call-by-reference languages where a locative to the variable *X*
> would be passed. Rather, the value of *X*, the object (A B C),
> is what is received by DELETE, hence the sometimes name call-by-value.
> It's the argument's value, not its reference, that is passed.
>

If I understand you correctly, the "call-by-reference languages" you are
referring to include c++ with its "&" reference parameter passing. I
can't seem to think of any others which would comply. (among the set
(smalltalk python java), that is - these all pass referents to the value
rather than to the variable).

Can you name any other "call-by-reference languages" ?

Thanks,
John

Duane Rettig

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
John Clonts <joh...@my-deja.com> writes:

> Kent M Pitman wrote:

> > That is, the arg passed is *not* what is traditionally meant in
> > call-by-reference languages where a locative to the variable *X*
> > would be passed. Rather, the value of *X*, the object (A B C),
> > is what is received by DELETE, hence the sometimes name call-by-value.
> > It's the argument's value, not its reference, that is passed.
> >
>
> If I understand you correctly, the "call-by-reference languages" you are
> referring to include c++ with its "&" reference parameter passing. I
> can't seem to think of any others which would comply. (among the set
> (smalltalk python java), that is - these all pass referents to the value
> rather than to the variable).
>
> Can you name any other "call-by-reference languages" ?

Fortran. _All_ arguments are passed by reference. In Fortran, if
you pass 0.0 into a subroutine as a variable, and you then change that
variable within the subroutine, your program gets pretty messed up,
because 0.0 is now no longer 0.0...

Erik Naggum

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
* John Clonts <joh...@my-deja.com>

| If I understand you correctly, the "call-by-reference languages" you
| are referring to include c++ with its "&" reference parameter passing.
| I can't seem to think of any others which would comply. [...] Can

| you name any other "call-by-reference languages"?

Pascal's VAR variables. Algol's and Simula's pass by NAME. Ada's OUT
arguments. The list is longer.

#:Erik
--
ALGORITHM: a procedure for solving a mathematical problem in a finite
number of steps that frequently involves repetition of an operation.
ALGOREISM: a procedure for solving an electoral problem in a finite
number of steps that frequently involves repetition of an operation.

Raymond Toy

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
>>>>> "Duane" == Duane Rettig <du...@franz.com> writes:

Duane> Fortran. _All_ arguments are passed by reference. In Fortran, if
Duane> you pass 0.0 into a subroutine as a variable, and you then change that
Duane> variable within the subroutine, your program gets pretty messed up,
Duane> because 0.0 is now no longer 0.0...

Is this still true (0.0 not being 0.0 anymore). I know long ago this
used to cause really cool bugs, but does it still happen? I quick and
dirty test with g77 gives me a segfault.

Ray

Duane Rettig

unread,
Nov 15, 2000, 3:00:00 AM11/15/00
to
Raymond Toy <t...@rtp.ericsson.se> writes:

It's true for f77 (g77). I don't keep up with the later versions, so
I don't know if, for example, f90 has addressed this. I would guess
not, nor would I think that Fortran pundits would even consider it to
be a language problem.

jos...@corporate-world.lisp.de

unread,
Nov 15, 2000, 7:28:53 PM11/15/00
to
In article <HBF.2000...@bombur.uio.no>,
Hallvard B Furuseth <h.b.fu...@usit.uio.no> wrote:

> Rainer Joswig <jos...@corporate-world.lisp.de> wrote:
> >
> > How about:
> >
> > (...)
> > (defun foo (n)
> > (let ((a n)
> > (b (1+ n)))
> > (declare (special a b))
>
> The original post wanted to avoid global variables.

Where do you see "global variables" in this example?
What you see is a use of non-global dynamic binding.

cbbr...@hex.net

unread,
Nov 15, 2000, 8:07:37 PM11/15/00
to
Greg Menke <gregm-n...@zxy.mindspring.com> writes:
> I have a defun thats getting pretty large, and would like to split
> it up. The problem is I have a bunch of local let variables which
> I'd like to pass into subordinate functions, where they are modified
> and retain the modified values upon return. I would rather avoid
> creating global variables, so am looking for a reasonable method for
> passing them by reference. The variables consist of a couple lists,
> a char or two and some numerics & strings.
>
> Or, if there is a preferred way to deal with this kind of situation,
> that would be fine too. A redesign of the program architecture,
> allowing an alternative approach would also be fine.
>
> I get the feeling I'm missing something obvious about how variables
> want to operate in Lisp. I apologize if this is an ill-considered
> question, all hints are cheerfully accepted.

Others have suggested trying to structure the data using DEFSTRUCT,
and then passing that single structure around.

Supposing there's a fairly motley bunch of data, this may be asking to
become a "package," essentially providing a new namespace that
implicitly provides a "structure" in which the various values can
reside.

Defining a package for this set of functions would provide a local
namespace which lets you define whatever set of "somewhat messy
sorta-local variables" you need, avoiding both the need to pass them
all around, as well as the need to worry about them conflicting with
"truly global" variables.

(defpackage "COOKIES"
(:use "COMMON-LISP")
(:export "COOKIE" "*email-addresses*" "*websites*"))
(in-package "COOKIES")
.. define all sorts of "cookie" stuff ...
(defun cookie () ;;; This is the "public" function
)

(defun work-fun-1 () ;;; Not seen outside
)
(defun work-fun-2 () ;;; Not seen outside
)
(defun work-fun-3 () ;;; Not seen outside
)

;;; Here are some variables that are usable by all the internal
;;; functions, but invisible outside...
(defvar *something* 25)
(defvar *something-else* 30)
(defvar *still-something-else* '(blah blah blah))


--
(concatenate 'string "cbbrowne" "@hex.net")

<http://www.ntlug.org/~cbbrowne/lisp.html>
When there's a will, I want to be in it.

Kent M Pitman

unread,
Nov 15, 2000, 11:08:28 PM11/15/00
to
John Clonts <joh...@my-deja.com> writes:

> If I understand you correctly, the "call-by-reference languages" you are
> referring to include c++ with its "&" reference parameter passing. I

> can't seem to think of any others which would comply. (among the set
> (smalltalk python java), that is - these all pass referents to the value

> rather than to the variable). Can you name any other "call-by-reference
> languages" ?

I assure you there have been others. Doesn't pascal? Certainly PL/I.
I'm pretty sure most Fortran dialects are call-by-reference, leading
if I recall to fascinating bugs where people have clobbered even
constants. In PL/I, people programmed defensively and put parentheses
around names of things they wanted to assure wouldn't get clobbered.
5 was a clobberable value, but (5) was a protected, computed value.

Languages used to be as plentiful as flowers in a field, and there were
myriad languages with any given characteristic. That you can only name
a couple of such languages nowadays is dreadfully sad, akin to the
extinction of beautiful animals that will never come again. Assembly
languages are, of course, call by reference.


Pierre R. Mai

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
cbbr...@hex.net writes:

> Others have suggested trying to structure the data using DEFSTRUCT,
> and then passing that single structure around.
>
> Supposing there's a fairly motley bunch of data, this may be asking to
> become a "package," essentially providing a new namespace that
> implicitly provides a "structure" in which the various values can
> reside.
>
> Defining a package for this set of functions would provide a local
> namespace which lets you define whatever set of "somewhat messy
> sorta-local variables" you need, avoiding both the need to pass them
> all around, as well as the need to worry about them conflicting with
> "truly global" variables.

While this is another often useful approach to the problem, you have
to be aware of the difference between structures and packages in this
regard: Since only one instance of any given package exists, there's
now only one "global" state of the whole module, so you have to be
careful to handle reentrance issues, that could arise because of
threading or down-ward funargs. To solve this in the package
approach, you have to essentially rebind all the variables in COOKIES,
whereas structure-based approaches can either pass around different
state objects, or rebind one global state variable (see
e.g. *random-state* in the standard).

Just something to be aware of...

Regs, Pierre.

--
Pierre R. Mai <pm...@acm.org> http://www.pmsf.de/pmai/
The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We cause accidents. -- Nathaniel Borenstein

Pekka P. Pirinen

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
Hallvard B Furuseth <h.b.fu...@usit.uio.no> writes:
> Of course you _could_ implement call-by-reference by hand, something
> like the code below.

Neat idea.

> (defmacro place-name (place)
> (if (let ((tmp (macroexpand place)))

Add environment arguments there, you never know.

> ;; This should test is (setf TMP) is OK, if I remembered how.
> (or (constantp tmp)
> (not (or (symbolp tmp)
> (consp tmp)))))

I would just write two versions of this with and without SETF, and let
the user worry, but if it was necessary, I'd test by:
(not (null (ignore-errors (macroexpand-1 `(setf ,place foo) env))))
and that still leaves some uncertainty if it expands to use SETF
function -- at least the worst you get would be a warning about a
missing function.

> (defun-named fun (arg1 (:named arg2) arg3 ...) ...)
> (fun v1 v2 v3)
> would expand to
> (defmacro fun (...) ... fun-wrapper ...)

> [...]


>
> at which point you have probably done a lot more work than
> a solution with simple closures or a struct or something:-)

And it would have been clearer to write it as a macro directly in the
first place, which is one reason why you don't see people using
call-by-reference in CL very much.
--
Pekka P. Pirinen
I have yet to see any problem, however complicated, which,
when you looked at it in the right way, did not become still
more complicated. - Poul Anderson

Greg Menke

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to

> call what Lisp does call-by-value, we just don't mean the kind of call-by-value
> that other languages use. That's why I prefer the call-by-identity name.

I like the call-by-identity idea. After thinking about it a bit and
experimenting, it works very well.

Thanks again,

Gregm

Joe Marshall

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
Kent M Pitman <pit...@world.std.com> writes:

> Assembly languages are, of course, call by reference.

What?! Sure, it is possible to write call by reference in assembler,
provided the hardware has some notion of it, but the bulk of the
commonly used operations in assembler code very clearly move values
around.

When you are calling a subroutine in assembly, you typically either
put the values in some registers or upon the stack. It is pretty rare
to compute the addresses of the values you wish to pass. The `load
effective address' and `push effective address' or their equivalent
instructions are typically used to compute the address of a value, and
these sort of operations are far less common than `push' or `move'.

Now it is true that addresses are often the values that are moved and
true that data structures are typically managed by pointers, but the
ultimate `variables' provided by the hardware are the registers, and
it is uncommon these days for the registers to even *have* addresses
that can be passed by reference.

I'd say that assembly languages are predominantly call by value.


-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----

Kent M Pitman

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
Joe Marshall <j...@content-integrity.com> writes:

>
> Kent M Pitman <pit...@world.std.com> writes:
>

> > Assembly languages are, of course, call by reference.
>

> What?! Sure, it is possible to write call by reference in assembler, [...]

As it is in PL/I and others. I didn't mean defaultly, sorry. The context
of the discussion was really cbr-capable...


Nils Goesche

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
Erik Naggum <er...@naggum.net> writes:

> * John Clonts <joh...@my-deja.com>


> | If I understand you correctly, the "call-by-reference languages" you
> | are referring to include c++ with its "&" reference parameter passing.

> | I can't seem to think of any others which would comply. [...] Can


> | you name any other "call-by-reference languages"?
>

> Pascal's VAR variables. Algol's and Simula's pass by NAME. Ada's OUT
> arguments. The list is longer.

I never understood what `call by name' means. Has it to do with
Lisp's special variables?
--
Nils Goesche
"Don't ask for whom the <CTRL-G> tolls."

Erik Naggum

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
* Nils Goesche <nils.g...@anylinx.de>

| I never understood what `call by name' means.

It means that the full expression in the caller is evaluated in the
callee, instead of actually passing in the object so evaluated. For
instance, if you pass in foo.bar[i], meaning the i'th slot in the bar
vector of the foo object, the evaluation that leads to this object is
performed in the callee with the prevailing values. That is, if you
also get passed in i by some means, you reference some other slot if
you change the value of i, even though none of this is visible in the
argument you actually reference. The idea is that you should be able
to modify the lexically named place in the caller from the callee as
if you _were_ the caller. Very clever implementation techniques are
required to implement this insanity correctly and usefully, not to
mention that code written with this feature used and abused east and
west is exceptionally exciting to debug.

| Has it to do with Lisp's special variables?

No relation at all. (Various deities be thanked, etc.)

Joe Marshall

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
Kent M Pitman <pit...@world.std.com> writes:

> Joe Marshall <j...@content-integrity.com> writes:
>
> >
> > Kent M Pitman <pit...@world.std.com> writes:
> >

> > > Assembly languages are, of course, call by reference.
> >

> > What?! Sure, it is possible to write call by reference in assembler, [...]
>
> As it is in PL/I and others. I didn't mean defaultly, sorry. The context
> of the discussion was really cbr-capable...

My apologies.

Lieven Marchand

unread,
Nov 16, 2000, 3:00:00 AM11/16/00
to
Duane Rettig <du...@franz.com> writes:

> Raymond Toy <t...@rtp.ericsson.se> writes:
>
> > >>>>> "Duane" == Duane Rettig <du...@franz.com> writes:
> >
> > Duane> Fortran. _All_ arguments are passed by reference. In Fortran, if
> > Duane> you pass 0.0 into a subroutine as a variable, and you then change that
> > Duane> variable within the subroutine, your program gets pretty messed up,
> > Duane> because 0.0 is now no longer 0.0...
> >
> > Is this still true (0.0 not being 0.0 anymore). I know long ago this
> > used to cause really cool bugs, but does it still happen? I quick and
> > dirty test with g77 gives me a segfault.
>
> It's true for f77 (g77). I don't keep up with the later versions, so
> I don't know if, for example, f90 has addressed this. I would guess
> not, nor would I think that Fortran pundits would even consider it to
> be a language problem.

It has never been a language feature you could rely on. But the
Fortran standard explicitly disallows constants for function args that
gets assigned to, so there is no language problem. If you invoke
undefined behaviour, you can't complain about what you get.

--
Lieven Marchand <m...@bewoner.dma.be>
Lambda calculus - Call us a mad club

Rob Warnock

unread,
Nov 17, 2000, 3:00:00 AM11/17/00
to
Erik Naggum <er...@naggum.net> wrote:
+---------------

| * Nils Goesche <nils.g...@anylinx.de>
| | I never understood what `call by name' means.
|
| It means that the full expression in the caller is evaluated in the
| callee, instead of actually passing in the object so evaluated.
| ...

| The idea is that you should be able to modify the lexically named
| place in the caller from the callee as if you _were_ the caller.
| Very clever implementation techniques are required to implement this
| insanity correctly and usefully...
+---------------

Which, interestingly enough, brings us back around to Lisp & Scheme,
since in many Algol implementations call-by-name was implemented by
passing the addresses of little pieces of code in the caller that would
implement the lexical environment and the evaluation of the argument form
in that environment, actually two addresses per argument -- a 0-arg "getter"
and a 1-arg "setter". These little pieces of code were called "thunks"
[see <URL:http://www.tuxedo.org/~esr/jargon/html/entry/thunk.html> for
a more detailed etymology], and the term survives today in Lisp & Scheme
meaning a closure of zero arguments.

By the way, Erik's example of calling "foo(i,a[i])", where args are
call-by-name and "foo" changes the value of its first arg, is usually
known as "Jensen's Device":

<URL:http://www.cs.sfu.ca/~cameron/Teaching/383/PassByName.html>
<URL:http://carbon.cudenver.edu/~traup/sp00/lecture/23.html>
<URL:http://www.math.grin.edu/~rebelsky/Courses/CS302/99S/Outlines/
outline.36.html>
<URL:http://www.cir.nus.edu.sg/~dkiong/compilers/98/03-229.html>

In Lisp, one might define a Jensen's Device generalized summation
function like so:

> (defun jsum (item-thunk index-setter start end)
(loop for index from start below end
do (funcall index-setter index)
summing (funcall item-thunk)))
>

Then you might use it to sum an array like this:

> (defvar v1 #(0 1 2 3))
> (let (i)
(jsum (lambda () (aref v1 i))
(lambda (x) (setf i x))
0
(length v1)))
6
>

But the "item-thunk" can easily be more complex. For example,
to do a dot-product between two vectors:

> (defvar v1 #(0 1 2 3))
> (defvar v2 #(7 5 -4 6))
> (let (i)
(jsum (lambda () (* (aref v1 i) (aref v2 i)))
(lambda (x) (setf i x))
0
(length v1)))
15
>


-Rob

-----
Rob Warnock, 31-2-510 rp...@sgi.com
Network Engineering http://reality.sgi.com/rpw3/
Silicon Graphics, Inc. Phone: 650-933-1673
1600 Amphitheatre Pkwy. PP-ASEL-IA
Mountain View, CA 94043

Joe Marshall

unread,
Nov 17, 2000, 3:00:00 AM11/17/00
to
rp...@rigden.engr.sgi.com (Rob Warnock) writes:

This sort of a trick need not be expensive, either. If you can inline
the call to jsum, and if your compiler has a modicum of intelligence
(it needn't be the hypothetical `sufficiently smart' compiler, but it
should know how to inline a literal lambda), then that last form
transforms like this:

(let (i)
(jsum (lambda () (* (aref v1 i) (aref v2 i)))
(lambda (x) (setf i x))
0
(length v1)))

inlined to:

(let (i)
(loop for index from 0 below (length v1)
do (funcall (lambda (x) (setf i x)) index)
summing (funcall (lambda () (* (aref v1 i) (aref v2 i))))))

Which should turn into

(let (i)
(loop for index from 0 below (length v1)
do (setf i index)
summing (* (aref v1 i) (aref v2 i))))

A slightly more clever compiler might notice that i and index can be
shared:
(loop for index from 0 below (length v1)
summing (* (aref v1 index) (aref v2 index)))

Erik Naggum

unread,
Nov 17, 2000, 3:00:00 AM11/17/00
to
* Joe Marshall <j...@content-integrity.com>

| A slightly more clever compiler might notice that i and index can be
| shared:
| (loop for index from 0 below (length v1)
| summing (* (aref v1 index) (aref v2 index)))

Wouldn't a slightly _less_ "clever" programmer do this right away?

Joe Marshall

unread,
Nov 17, 2000, 3:00:00 AM11/17/00
to
Erik Naggum <er...@naggum.net> writes:

> * Joe Marshall <j...@content-integrity.com>
> | A slightly more clever compiler might notice that i and index can be
> | shared:
> | (loop for index from 0 below (length v1)
> | summing (* (aref v1 index) (aref v2 index)))
>
> Wouldn't a slightly _less_ "clever" programmer do this right away?

Obviously, but that wouldn't illustrate "Jensen's Device".

Joe Marshall

unread,
Nov 17, 2000, 3:00:00 AM11/17/00
to
Erik Naggum <er...@naggum.net> writes:

> * Joe Marshall <j...@content-integrity.com>


> | Obviously, but that wouldn't illustrate "Jensen's Device".
>

> So does it have any uses that sort of, you know, pay off?

Yes. It pays off when the callee (in the example the jsum function)
has complicated functionality, and you have a decent encapsulation.
In this example, the jsum function is no more powerful than the loop
it replaces.

A better example might be a generalized transitive-closure function.
It would take as arguments an initial node, a function that generates
the descendent nodes, a function that tests for equivalence, a
function to stepwise combine a state and a node, and an initial state.

You could use this to do traversal of any general graphlike data
structure, from traversing a file system, to traversing a compiler
dependency graph.

The difficulty with using this solution directly is that the core loop
within the transitive closure operation is calling out to lexical
closures to get the work done. Not only does this add a lot of
unnecessary overhead, but it makes it impossible for the compiler to
discover optimizations (unless the compiler can optimize across
function boundaries).

In the cases where the functions passed in are literal lambdas, you
would like to inline the core loop in the caller, and inline the
function bodies within the loop. This will avoid funcalling the
closure, avoid having to indirect through an environment to fetch the
variables, and allow the compiler to infer the types of the objects
that you are mapping over.


> What good is illustrating something with an example that's clearly
> inferior to a much simpler solution?

A simple example may make it easier to understand the concept where a
more realistic example may hide the concept in a jumble of irrelevant
detail.

In any case, I did not choose the example.

> Given the language feature, it _was_ quite clever, however.
> Lacking that language feature, it is moderately silly to reinvent
> it just so you can do in very complex ways what used to be very
> simple, at least as the programmer saw it at the source code
> level.

But isn't that what modern languages such as Java do?

Seriously, though, you are right. It is silly to use hairy techniques
where simple ones will do.

Bruce Hoult

unread,
Nov 17, 2000, 4:53:38 PM11/17/00
to
In article <31833114...@naggum.net>, Erik Naggum <er...@naggum.net>
wrote:

> * John Clonts <joh...@my-deja.com>
> | If I understand you correctly, the "call-by-reference languages" you
> | are referring to include c++ with its "&" reference parameter passing.
> | I can't seem to think of any others which would comply. [...] Can
> | you name any other "call-by-reference languages"?
>
> Pascal's VAR variables. Algol's and Simula's pass by NAME. Ada's OUT
> arguments. The list is longer.

My understanding of Ada's OUT and INOUT parameters (from reading
Steelman many years ago only, alas, I've never actually seen a compiler
for it) was that they could be implemented either by reference or
value/return, and it was an error to depend on the implementation.

-- Bruce

Bruce Hoult

unread,
Nov 17, 2000, 4:55:54 PM11/17/00
to
In article <31833897...@naggum.net>, Erik Naggum <er...@naggum.net>
wrote:

> * Nils Goesche <nils.g...@anylinx.de>


> | I never understood what `call by name' means.
>
> It means that the full expression in the caller is evaluated in the

> callee, instead of actually passing in the object so evaluated. For
> instance, if you pass in foo.bar[i], meaning the i'th slot in the bar
> vector of the foo object, the evaluation that leads to this object is
> performed in the callee with the prevailing values. That is, if you
> also get passed in i by some means, you reference some other slot if
> you change the value of i, even though none of this is visible in the

> argument you actually reference. The idea is that you should be able


> to modify the lexically named place in the caller from the callee as
> if you _were_ the caller. Very clever implementation techniques are

> required to implement this insanity correctly and usefully, not to
> mention that code written with this feature used and abused east and
> west is exceptionally exciting to debug.

All you have to do is pass a function that evaluates the expression.
This is common in nearly every language for things such as sort
functions, and certainly *extremely* common in languges such as Lisp
which allow anonymous lambda functions.

-- Bruce

Erik Naggum

unread,
Nov 17, 2000, 9:01:25 PM11/17/00
to
* Bruce Hoult

| My understanding of Ada's OUT and INOUT parameters (from reading
| Steelman many years ago only, alas, I've never actually seen a compiler
| for it) was that they could be implemented either by reference or
| value/return, and it was an error to depend on the implementation.

My point here was simply to enumerate some languages that support some
useful concept of references, not quibble over implementations of that
concept. I can retract Ada from the list of languages if that is what
_you_ want to accomplish.

Erik Naggum

unread,
Nov 17, 2000, 9:15:06 PM11/17/00
to
* Joe Marshall <j...@content-integrity.com>
| Obviously, but that wouldn't illustrate "Jensen's Device".

So does it have any uses that sort of, you know, pay off?

What good is illustrating something with an example that's clearly
inferior to a much simpler solution? Given the language feature, it


_was_ quite clever, however. Lacking that language feature, it is
moderately silly to reinvent it just so you can do in very complex
ways what used to be very simple, at least as the programmer saw it at
the source code level.

#:Erik

Erik Naggum

unread,
Nov 17, 2000, 9:09:28 PM11/17/00
to
* Bruce Hoult <br...@hoult.org>

| All you have to do is pass a function that evaluates the expression.
| This is common in nearly every language for things such as sort
| functions, and certainly *extremely* common in languges such as Lisp
| which allow anonymous lambda functions.

You mean (lexical) closures, not just anonymous lambda functions.
That's a very clever implementation technique, because using something
like that is _not_ spelled out as we know to be closures today in the
literature of these languages at the time, like the implementation of
closures is not spelled out in the Common Lisp standard, either, only
their semantics. E.g., I have seen the C-based Simula compiler go to
some rather astonishing lengths _not_ to implement closures. Plus, I
thought it would just be confusing to try to mix in closures with this
design, since the closures are not accessible in the language, and any
means of achieving same is acceptable, kinda like OUT arguments in Ada
if you're in quibble mode.

Erik Naggum

unread,
Nov 18, 2000, 12:14:20 AM11/18/00
to
* Joe Marshall <j...@content-integrity.com>

| A simple example may make it easier to understand the concept where a
| more realistic example may hide the concept in a jumble of irrelevant
| detail.

I think simple examples are counter-productive because they do not
show the _normal_ complexity of the situation in which something makes
sense. That's why "hello, world" programs are so fantastically useless.

We have a long tradition of suffering from people who learned Lisp as
an example of something rather trivial that never explained why Lisp
was a good choice -- anything else would be a good choice, too -- nor
when Lisp would be a good choice.

| But isn't that what modern languages such as Java do?

Yeah, that's the "misery loves company" argument for doing something
painful.

Hallvard B Furuseth

unread,
Nov 19, 2000, 6:14:05 PM11/19/00
to
pe...@harlequin.co.uk (Pekka P. Pirinen) writes:
>Hallvard B Furuseth <h.b.fu...@usit.uio.no> writes:
>
>> ;; This should test is (setf TMP) is OK, if I remembered how.
>> (or (constantp tmp)
>> (not (or (symbolp tmp)
>> (consp tmp)))))
>
> I would just write two versions of this with and without SETF, and let
> the user worry,

Good point. That's what sidetracked me in the first place; I knew there
was something obvious I was forgetting. To get call by reference
instead of name, I could just have made a sort of SETF implementation.

(defconstant *place-ref-default* (gensym))

(defmacro place-ref (&rest places &environment env)
(multiple-value-bind (vars vals store-vars store-form access-form)
(get-setf-expansion (if (cdr places) `(values ,@places) (car places))
env)
(assert (= (length store-vars) (length places)))
`(let (,@(mapcar #'list vars vals))
#'(lambda (&optional (,(car store-vars) *place-ref-default*)
,@(cdr store-vars))
(if (eq ,(car store-vars) *place-ref-default*)
,access-form
,store-form)))))

(defmacro const-ref (&rest constants)
(let ((binds (mapcar (lambda (c) (list (gensym) c)) constants)))
`(let (,@binds)
#'(lambda () (values ,@(mapcar #'car binds))))))


>> (defun-named fun (arg1 (:named arg2) arg3 ...) ...)
>> (fun v1 v2 v3)
>> would expand to
>> (defmacro fun (...) ... fun-wrapper ...)
>> [...]
>>
>> at which point you have probably done a lot more work than
>> a solution with simple closures or a struct or something:-)
>
> And it would have been clearer to write it as a macro directly in the
> first place, which is one reason why you don't see people using
> call-by-reference in CL very much.

True, but he did ask:-)

--
Hallvard

Hallvard B Furuseth

unread,
Nov 19, 2000, 6:18:05 PM11/19/00
to
Rainer Joswig wrote:
>In article <HBF.2000...@bombur.uio.no>,

>>> (defun foo (n)
>>> (let ((a n)
>>> (b (1+ n)))
>>> (declare (special a b))
>>
>> The original post wanted to avoid global variables.
>
> Where do you see "global variables" in this example?
> What you see is a use of non-global dynamic binding.

Sorry, I just assumed that's what he meant.

--
Hallvard

Tim Bradshaw

unread,
Nov 20, 2000, 3:00:00 AM11/20/00
to
Erik Naggum <er...@naggum.net> writes:
>
> We have a long tradition of suffering from people who learned Lisp as
> an example of something rather trivial that never explained why Lisp
> was a good choice -- anything else would be a good choice, too -- nor
> when Lisp would be a good choice.
>

I think it's hard to know how to get away from this. The two
environments where I've taught Lisp are week (or thereabouts) long
full-time courses to people who can usually program in another
language, and n-lecture-per-week courses to people who really can't
(but think they can in the usual student way). I think these typify
the two ways languages are taught. The week-long courses are much
better both for the teacher and (I think) for the student. But for
neither of these can you teach non-trivial examples.

I think that the way to teach programming languages (not just Lisp...)
is to realise that they're basically a craft skill, and you learn them
by the way people generally learn craft skills -- you do an
apprenticeship for several years (this is how, for instance, medicine
is taught, where people are `housemen' (might be different term in US)
in hospitals for 3 years or so). In an environment like that you can
see non-trivial examples, because you're likely working on part of
one. Unfortunately I think it's hugely unlikely that the notion of
doing an apprenticeship will ever catch on, since the culture is so
opposed to it, so we'll always end up tryiong to teach complex
concepts with trivial examples.

--tim

Pekka P. Pirinen

unread,
Nov 20, 2000, 3:00:00 AM11/20/00
to
Hallvard B Furuseth <h.b.fu...@usit.uio.no> writes:
> (defmacro place-ref (&rest places &environment env)
> [...]

> `(let (,@(mapcar #'list vars vals))
> #'(lambda (&optional (,(car store-vars) *place-ref-default*)

But now it's call-by-reference, rather than call-by-name, since the
subforms of the places are evaluated in the caller, rather than at
each access.

BTW, to my eye a supplied-p parameter would be slightly more stylish
than *PLACE-REF-DEFAULT*.
--
Pekka P. Pirinen, Adaptive Memory Management Group, Harlequin Limited
One thing that I have noticed is that all of these conspiracy theories depend
on the perpetrators being endlessly clever. I think you'll find the facts
also work if you assume everyone is endlessly stupid. - Brian E. Moore

Christian Lynbech

unread,
Nov 22, 2000, 3:00:00 AM11/22/00
to
>>>>> "Kent" == Kent M Pitman <pit...@world.std.com> writes:

Kent> Languages used to be as plentiful as flowers in a field, and there were
Kent> myriad languages with any given characteristic. That you can only name
Kent> a couple of such languages nowadays is dreadfully sad, akin to the
Kent> extinction of beautiful animals that will never come again. Assembly
Kent> languages are, of course, call by reference.

I was once browsing through ther proceedings of the first HOPL
(History of Programming Languages) conference. It has a graph
depicting languages by time of invention (together with how they
influenced each other).

Seeing that graph it occurred to me how *few* languages actually are
downright extinct. Just witness the amount of code still in use,
written in early languages such as Fortran, Cobol or LISP.

Languages may go out of style, but to vanish completely is a rare event.

The HOPL proceedings are highly recommendable reading, especially for
people like myself who just weren't around in the CS field in those
early days.


------------------------+-----------------------------------------------------
Christian Lynbech | Ericsson Telebit, Skanderborgvej 234, DK-8260 Viby J
Phone: +45 8938 5244 | email: c...@tbit.dk
Fax: +45 8938 5101 | web: www.ericsson.com
------------------------+-----------------------------------------------------
Hit the philistines three times over the head with the Elisp reference manual.
- pet...@hal.com (Michael A. Petonic)

gde...@my-deja.com

unread,
Nov 22, 2000, 3:00:00 AM11/22/00
to
Bruce Hoult:

> My understanding of Ada's OUT and INOUT parameters (from reading
> Steelman many years ago only, alas, I've never actually seen a compiler
> for it) was that they could be implemented either by reference or
> value/return, and it was an error to depend on the implementation.

Right! ;-) The fun is to mimic CAR/CDR with a'first and a(a'first+
1..a'last) but well.
If you want to see what an Ada compiler looks like, download
GNAT or ObjectAda! There are others of course (also ISO compliant).
Some links on my page...
______________________________________________________
Gautier -- http://members.nbci.com/gdemont/gsoft.htm


Sent via Deja.com http://www.deja.com/
Before you buy.

Hallvard B Furuseth

unread,
Nov 27, 2000, 10:17:08 PM11/27/00
to
pe...@harlequin.co.uk (Pekka P. Pirinen) writes:
>Hallvard B Furuseth <h.b.fu...@usit.uio.no> writes:
>> (defmacro place-ref (&rest places &environment env)
>> [...]

>> `(let (,@(mapcar #'list vars vals))
>> #'(lambda (&optional (,(car store-vars) *place-ref-default*)
>
> But now it's call-by-reference, rather than call-by-name,

Yes, that was the original request, and what I originally wanted to do.

> BTW, to my eye a supplied-p parameter would be slightly more stylish
> than *PLACE-REF-DEFAULT*.

OK.

--
Hallvard

William B. Clodius

unread,
Dec 4, 2000, 3:00:00 AM12/4/00
to

Duane Rettig wrote:
> <snip>


> Fortran. _All_ arguments are passed by reference.

Not true. Fortran does not define in detail the semantics of its
argument passing mechanism for general variables, and never has (i.e.,
not in Fortran (I), II, IV, 66, 77, 90 or 95). (Actually 90 and 95
provide more detailed semantics for arguments with the POINTER and
TARGET attributes.) Instead it restricts legal code to a subset that is
well defined for almost any argument passing mechanism. In particular if
an argument is accessible under more than one name (i.e., passed as an
argument) it cannot be modified in legal code. This allows
copy-in/copy-out (in arbitrary order with respect to the argument list)
as well as call by reference. It also allows optimizations involving
copying values to and from registers that are illegal under true call by
reference except after interprocedural analyses that (for true call by
reference) can involve solving the halting problem.

> In Fortran, if


> you pass 0.0 into a subroutine as a variable, and you then change that

> variable within the subroutine, your program gets pretty messed up,

> because 0.0 is now no longer 0.0...

> <snip>

As someone else noted this is illegal Fortran. Several tools, e.g.,
FTNCHEK, will catch this problem, and recent versions of the standard
have made it easier for compilers to detect this problem as part of
their standard analyses (provided the programmer makes use of the
additions to the language).

Reply all
Reply to author
Forward
0 new messages