From: jonathan  cano
Subject: newbie question: immutable cons?
Date: 
Message-ID: <1134438274.930348.119100@g43g2000cwa.googlegroups.com>
Does common lisp allow one to create a cons whose car and cdr can not
be modified?

It appears that (defconstant ...) doesn't quite do what I want.
Example:

    top level> (defconstant aa 7)
    top level> (setf aa 8)                      ; error

    top level> (defconstant cc (cons 1 2))
    top level> (setf cc (cons 3 4)              ; error
    top level> (setf cc 69)                     ; error

The behavior above is well and good but what I want is for the forms
below to result in errors too.

    top level> (setf (car cc) 3)        ; no errors here, I'm sad
    top level> (setf (cdr cc) 3)        ; to say.

There doesn't seem to be a (constant-cons (car form) (cdr form)) that
would result in errors like this

    top level> (defconstant cc (constant-cons 1 2))
    top level> (setf (car cc) 3)        ; error
    top level> (setf (cdr cc) 3)        ; error

I've googled a little bit and looked around the CLHS but I haven't
found this capability.  Based on some of the related threads I've seen
I'm guessing the common lisp does not specify a way to create an
immutable cons.  Is this correct?  If not, please enlighten me.

Related questions:
* if such a capability is not currently specified, is it coming soon
  e.g. in the next standard?
* do any popular common lisp implementations provide such a capability
  as an extension to the language (e.g. corman lisp, CLISP, CMU Lisp)?

Regards,
  --fj

FYI: I'm currently playing with "GNU CLISP 2.35"

From: Peter Seibel
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <m2u0ddzrbr.fsf@gigamonkeys.com>
"jonathan  cano" <······@gmail.com> writes:

> Does common lisp allow one to create a cons whose car and cdr can not
> be modified?
>
> It appears that (defconstant ...) doesn't quite do what I want.
> Example:
>
>     top level> (defconstant aa 7)
>     top level> (setf aa 8)                      ; error
>
>     top level> (defconstant cc (cons 1 2))
>     top level> (setf cc (cons 3 4)              ; error
>     top level> (setf cc 69)                     ; error
>
> The behavior above is well and good but what I want is for the forms
> below to result in errors too.
>
>     top level> (setf (car cc) 3)        ; no errors here, I'm sad
>     top level> (setf (cdr cc) 3)        ; to say.
>
> There doesn't seem to be a (constant-cons (car form) (cdr form)) that
> would result in errors like this
>
>     top level> (defconstant cc (constant-cons 1 2))
>     top level> (setf (car cc) 3)        ; error
>     top level> (setf (cdr cc) 3)        ; error
>
> I've googled a little bit and looked around the CLHS but I haven't
> found this capability.  Based on some of the related threads I've seen
> I'm guessing the common lisp does not specify a way to create an
> immutable cons.  Is this correct? 

Yes. You can, of course, define your own data type that is just like a
cons but with immutability. However you won't be able to use them with
CL:CAR and CL:CDR.

> Related questions:
> * if such a capability is not currently specified, is it coming soon
>   e.g. in the next standard?

No. And the phrase to describe when "the next standard" will arrive is
spelled "when hell freezes over". ;-) But that's okay because most
languages don't even have *one* standard and they seem to do
okay.

> * do any popular common lisp implementations provide such a capability
>   as an extension to the language (e.g. corman lisp, CLISP, CMU Lisp)?

Not that I know of. Henry Baker, author of several interesting papers
on Lisp has written a paper on the desirability of immutable conses,
though more from a perspective of the compilation optimizations it
allows. However Lispers have gotten by for neigh on 50 years now
without them so I don't see any implementations rushing to implemnet
this. Out of curiosity, why do you want them?

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: funkyj
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <1134447378.956413.209650@g44g2000cwa.googlegroups.com>
> Out of curiosity, why do you want them?

First off, I come from a statically typed, defensive coding background.
 I want a const-cons for the same reason the language has (defconstant
...) -- I want to catch when some code does something I don't want it
to.

(let ((cc (cons 12 2)))
   (defun cc-get  ()  cc)
   (defun ccp   (obj) (eq obj cc)))

this code is very efficient (using eq rather than equal) BUT if someone
goes and does

   top> (nconc  (cc-get) '(5 6 7 8 9 10))

Sure, I could code 'cc-get' like this

  (defun cc-get () (cons 12 2))

and that would minimize the damage done by 'nconc' above but what I
REALLY want is for the caller not to be able to modify the object
returned by cc-get.  The advantage of 'const-cons' is the same as
'defconstant' -- you can catch logic errors when they occur rather than
10 minutes later when the consequence of naughty behavior causes
something to blow up.

Now in the example above, the performance advantage of having a
singleton and getting to use eq is not that much of a win over equal
but if I had a much larger structure I wanted to make const then having
a 'const-list', 'const-cons' etc makes a difference.

Presumably 'defconstant' wasn't in McCarthy's original paper but
apparently there were enought people who thought it a useful addition
that it got put into the Common Lisp spec.  I'm sure all the programs
that currently make use of 'defconstant' could be made to work without
it but that doesn't mean 'defconstant' is useless.

I can LIVE without const-cons and other consts things but it would be
nice if they were around.

NIL is a singleton -- there is only one nil:

   top> (eq '() '())  ==> T

and

  top> (nconc nil '(1 2))

doesn't change the meaning of nil.  Why shouldn't I be able to make
more complex singletons that are also immutable?

Sorry for the rambling style but I'm composing this in a web browser
window (rather than in emacs) so I am not proof reading and editing
much.

BTW, thanks for the response,
  --jfc
From: Pascal Bourguignon
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <87irtt38na.fsf@thalassa.informatimago.com>
"funkyj" <······@gmail.com> writes:

>> Out of curiosity, why do you want them?
>
> First off, I come from a statically typed, defensive coding background.
>  I want a const-cons for the same reason the language has (defconstant
> ...) -- I want to catch when some code does something I don't want it
> to.

You've got a misconception here.
DEFCONSTANT doesn't prevent you to change the value of the constant binding.

It is _you_, the programmer, telling to the compiler that _you_ won't
be changing it, so the compiler can, if it so wants, copy the value
and otherwise optimize it out directly in the generated code.



Note in the following, how half gets its value changed, and how this
particular implementation use the changed value in the compiled code:

[113]> (defconstant half 1/2)
HALF
[114]> (defun twice (x) (/ x half))
TWICE
[115]> (compile 'twice)
TWICE ;
NIL ;
NIL
[116]> (defconstant half 1/3)
WARNING: (DEFCONSTANT HALF 1/3) redefines the constant HALF. Its old value was
          1/2
         .
HALF
[117]> half
1/3
[118]> (twice 3)
9
[119]> 

It's _your_ fault if twice doesn't return 6 here, not the implementation's.



This is for this reason that there is the convention of naming these
so-called "constant" variables with + signs:

(defconstant +half+ 1/2)

...

(setf +half+ 1/3) ; oh oh!



> (let ((cc (cons 12 2)))
>    (defun cc-get  ()  cc)
>    (defun ccp   (obj) (eq obj cc)))
>
> this code is very efficient (using eq rather than equal) BUT if someone
> goes and does
>
>    top> (nconc  (cc-get) '(5 6 7 8 9 10))
>
> Sure, I could code 'cc-get' like this
>
>   (defun cc-get () (cons 12 2))
>
> and that would minimize the damage done by 'nconc' above but what I
> REALLY want is for the caller not to be able to modify the object
> returned by cc-get.  

But the caller didn't _change_ the object returned by cc-get:

[119]> (let ((cc (cons 12 2)))
         (defun cc-get  ()  cc)
         (defun ccp   (obj) (eq obj cc)))
CCP
[120]> (defvar *the-only-one-cc* (cc-get))
*THE-ONLY-ONE-CC*
[121]> (nconc  (cc-get) '(5 6 7 8 9 10))
(12 5 6 7 8 9 10)
[122]> (eq *the-only-one-cc* (cc-get))
T
[123]> 

It changed the state of the cons, but didn't change the cons itself.


> The advantage of 'const-cons' is the same as
> 'defconstant' -- you can catch logic errors when they occur rather than
> 10 minutes later when the consequence of naughty behavior causes
> something to blow up.

You can still do it, you have all the tools to do it, closures, objets, etc.  


> Now in the example above, the performance advantage of having a
> singleton and getting to use eq is not that much of a win over equal
> but if I had a much larger structure I wanted to make const then having
> a 'const-list', 'const-cons' etc makes a difference.
>
> Presumably 'defconstant' wasn't in McCarthy's original paper but
> apparently there were enought people who thought it a useful addition
> that it got put into the Common Lisp spec.  I'm sure all the programs
> that currently make use of 'defconstant' could be made to work without
> it but that doesn't mean 'defconstant' is useless.

DEFCONSTANT is useful, but not to prevent the programmer shoot his own foot.



What you want is immutable objects.  This isn't specified by the
standard.  But any implementation is allowed to make all objects read
as program source immutable: the mere use of QUOTE can have the effect
you're wanting.

(defvar *my-own-constant-cons* '(12 2))
(setf (car *my-own-constant-cons*) 3) --> BANG!



If your implementation allows the mutation of source objects, perhaps
you should choose another implementation, or finance the addition of
this feature to your implementation, or implement it yourself, or
perhaps what you need is a clint?

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

"Indentation! -- I will show you how to indent when I indent your skull!"
From: Larry Clapp
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <slrndpsiok.bev.larry@theclapp.ddts.net>
On 2005-12-13, Peter Seibel <·····@gigamonkeys.com> wrote:
> "jonathan  cano" <······@gmail.com> writes:
>> Does common lisp allow one to create a cons whose car and cdr can
>> not be modified?
>>
[snip]
>> what I want is for the forms below to result in errors too.
>>
>>     top level> (setf (car cc) 3)        ; no errors here, I'm sad
>>     top level> (setf (cdr cc) 3)        ; to say.
>>
>> There doesn't seem to be a (constant-cons (car form) (cdr form))
>> that would result in errors like this
>>
>>     top level> (defconstant cc (constant-cons 1 2))
>>     top level> (setf (car cc) 3)        ; error
>>     top level> (setf (cdr cc) 3)        ; error
>>
>> I've googled a little bit and looked around the CLHS but I haven't
>> found this capability.  Based on some of the related threads I've
>> seen I'm guessing the common lisp does not specify a way to create
>> an immutable cons.  Is this correct? 
>
> Yes. You can, of course, define your own data type that is just like
> a cons but with immutability. However you won't be able to use them
> with CL:CAR and CL:CDR.

To expand on Peter's answer:

  (defpackage #:larrys-cl
    (:use #:common-lisp)
    (:shadow #:car #:cdr #:slot-value))

  (in-package #:larrys-cl)

  (defclass constant-cons ()
    ((car :initarg :car :reader car)
     (cdr :initarg :cdr :reader cdr)))

  (defun constant-cons (car cdr)
    (make-instance 'constant-cons :car car :cdr cdr))

  ;;; pass-thru methods
  (defmethod car ((object t))
    (cl:car object))

  (defmethod cdr ((object t))
    (cl:cdr object))

  (defmethod (setf car) (new-value (place t))
    (setf (cl:car place) new-value))

  (defmethod (setf cdr) (new-value (place t))
    (setf (cl:cdr place) new-value))

  ;;; throw errors
  (defmethod (setf car) (new-value (place constant-cons))
    (error "can't setf car of a constant-cons"))

  (defmethod (setf cdr) (new-value (place constant-cons))
    (error "can't setf cdr of a constant-cons"))

  (defmethod slot-value (object slot)
    (cl:slot-value object slot))

  (defmethod (setf slot-value) (new-value (place t) slot)
    (setf (cl:slot-value place slot) new-value))

  (defmethod (setf slot-value) (new-value (place constant-cons) slot)
    (error "can't setf slot-value of a constant-cons"))

  ;;; Similarly, if you want, with rplaca and rplacd.  Or just let
  ;;; them throw errors normally, since they work only with CONSes and
  ;;; CONSTANT-CONSes aren't CONSes.

Test cases:

  LARRYS-CL> (defvar cc (constant-cons 1 2))

  CC
  LARRYS-CL> (car cc)

  1
  LARRYS-CL> (setf (car cc) 3)

  Error in function (PCL:FAST-METHOD (SETF CAR) (T CONSTANT-CONS)):
     can't setf car of a constant-cons
  <etc>

  LARRYS-CL> (setf (cdr cc) 3)

  Error in function (PCL:FAST-METHOD (SETF CDR) (T CONSTANT-CONS)):
     can't setf cdr of a constant-cons
  <etc>

  LARRYS-CL> (setf (slot-value cc 'car) 3)

  Error in function (PCL:FAST-METHOD (SETF SLOT-VALUE) (T CONSTANT-CONS T)):
     can't setf slot-value of a constant-cons
  <etc>

> Out of curiosity, why do you want them?

I second this.  Why do you want them?

-- Larry
From: funkyj
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <1134458405.668124.191290@g44g2000cwa.googlegroups.com>
>>>> "SB>" == Peter Seibel writes:

    SB> Yes. You can, of course, define your own data type that is
    SB> just like a cons but with immutability. However you won't
    SB> be able to use them with CL:CAR and CL:CDR.

This didn't sink in the first time I read it but thanks to Larry Clapp
and his extensive example:

>>>> "LC>" == Larry Clapp writes:

    LC> To expand on Peter's answer:
    LC>
    LC>   (defpackage #:larrys-cl
    LC>     (:use #:common-lisp)
    LC>     (:shadow #:car #:cdr #:slot-value))
    LC>
    LC>   (in-package #:larrys-cl)
    LC>
    LC>   (defclass constant-cons ()
    LC>     ((car :initarg :car :reader car)
    LC>      (cdr :initarg :cdr :reader cdr)))

        [...]

    LC>   LARRYS-CL> (setf (cdr cc) 3)
    LC>
    LC>   Error in function (PCL:FAST-METHOD (SETF CDR) (T
CONSTANT-CONS)):
    LC>   can't setf cdr of a constant-cons <etc>

        [...]


    SB> Out of curiosity, why do you want them?
    LC> I second this.  Why do you want them?

The root of our misunderstanding is my ignorance.  Being new to the
language (I'm on page 57 of the book _ANSI Common Lisp_) I don't know
the capabilities of the language or even how to properly ask for what
I really want.

I'm not particularly wedded to the idea of the const-cons as the only
way to do what I want to do.  It was simply the most natural seeming
solution to me.

Now that I'v seen your sample code (Larry) I now know of at least one
way to create an immutable object that doesn't involve a 'const-cons'
language primitive. (Skipping ahead in my book for a moment I see
'defclass' and 'defmethod' mentioned in the CLOS chapter).

Thanks everyone (Peter Seibel, Pascal Bourguignon, Larry Clapp) for
your patience and help.  

Regards,
  --fj
From: Pascal Costanza
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <407bcvF18oqmmU1@individual.net>
funkyj wrote:

> The root of our misunderstanding is my ignorance.  Being new to the
> language (I'm on page 57 of the book _ANSI Common Lisp_) I don't know
> the capabilities of the language or even how to properly ask for what
> I really want.
> 
> I'm not particularly wedded to the idea of the const-cons as the only
> way to do what I want to do.  It was simply the most natural seeming
> solution to me.

If you don't insist on using the same names as specified in ANSI Common 
Lisp, you can get the same effects in a more straightforward way by just 
defining classes with your own names. That's typically preferable unless 
you really want to change the Common Lisp language for some reason.

Here is another version that uses a struct:

? (defstruct kons
     (kar nil :read-only t)
     (kdr nil :read-only t))
KONS
? (defvar *k* (make-kons :kar 42 :kdr 4711))
*K*
? (kons-kar *k*)
42
? (kons-kdr *k*)
4711
? (setf (kons-kar *k*) 666)
;Compiler warnings :
;   Undefined function SETF::|COMMON-LISP-USER::KONS-KAR|, in an 
anonymous lambda form.
 > Error: Undefined function SETF::|COMMON-LISP-USER::KONS-KAR| called 
with arguments (666 #S(KONS :KAR 42 :KDR 4711)) .


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: funkyj
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <1134459134.152067.56430@g47g2000cwa.googlegroups.com>
>>>> "PC" == Pascal Constanza writes:

PC> Here is another version that uses a struct:

   [...]

beautiful!  Thank you.

  --fj
From: Richard Fateman
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <sjCnf.37076$tV6.31998@newssvr27.news.prodigy.net>
maybe what you would like is "unique" conses, which are
not only immutable, but share memory with all conses
with the same car and cdr.
This "ucons" is implemented by storing all conses in
a hash table, and when a "new" ucons is about to be
created, it is looked up in the hash table.

This does have applications, since the uconses may
in fact be roots of huge structures. you can compare two
huge structures with EQ instead of EQUAL, then.

RJF
From: funkyj
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <1134495972.772604.279550@g49g2000cwa.googlegroups.com>
Richard Fateman writes:

> maybe what you would like is "unique" conses, which are
> not only immutable, but share memory with all conses
> with the same car and cdr.

Yes.  That is very much along the lines of my original thinking.

Now I have so many implementation choices I don't know which one to use
:^)

Thanks,
  --fj
From: Pascal Costanza
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <408h9pF188njsU2@individual.net>
funkyj wrote:
> Richard Fateman writes:
> 
> 
>>maybe what you would like is "unique" conses, which are
>>not only immutable, but share memory with all conses
>>with the same car and cdr.
> 
> 
> Yes.  That is very much along the lines of my original thinking.
> 
> Now I have so many implementation choices I don't know which one to use
> :^)

Oh, that's easy. Just take the best one. ;)


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: justinhj
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <1134503092.340189.16510@z14g2000cwz.googlegroups.com>
Pascal Costanza wrote:
> funkyj wrote:
> > Richard Fateman writes:
> >
> >
> >>maybe what you would like is "unique" conses, which are
> >>not only immutable, but share memory with all conses
> >>with the same car and cdr.
> >
> >
> > Yes.  That is very much along the lines of my original thinking.
> >
> > Now I have so many implementation choices I don't know which one to use
> > :^)
>
> Oh, that's easy. Just take the best one. ;)
>
>
> Pascal
>
> --
> My website: http://p-cos.net
> Closer to MOP & ContextL:
> http://common-lisp.net/project/closer/

Am I missing something when I say that if you want a list that is not
changeable, do you really need it to be a list at all?

An array would be just as good.

Justin
From: Kaz Kylheku
Subject: Re: newbie question: immutable cons?
Date: 
Message-ID: <1134599952.248332.237300@g49g2000cwa.googlegroups.com>
justinhj wrote:
> Am I missing something when I say that if you want a list that is not
> changeable, do you really need it to be a list at all?
>
> An array would be just as good.

No. You can't use one array as the tail end of another array (without
some additional structure on top of arrays). You also can't take the
tail of an array and pretend it's the entire array (not without
allocating another array and displacing it to that original one).

Lisp has lists that are not changeable, namely quoted literals. These
are often used in these ways.