Re: Ten Commandments for Lisp Programmers

From: Pascal Bourguignon (spam_at_thalassa.informatimago.com)
Date: 08/24/04


Date: 24 Aug 2004 00:17:05 +0200


"Coby Beck" <cbeck@mercury.bc.ca> writes:

> "Pascal Bourguignon" <spam@thalassa.informatimago.com> wrote in message
> news:87657a2do3.fsf@thalassa.informatimago.com...
> > nikodemus@random-state.net writes:
> > > "7. Thou shalt not suffer dangling pointers or references to be
> > > harbored
> > > within your objects. These are nefarious and precarious agents of
> > > random
> > > and wanton destruction. While ye may escape their wrath, thy fellow
> > > programmer may verily be ravaged by them."
> > >
> > > Bwhahaaa. ;) "Here's a nickel, go and buy yourself a garbage collector."
> >
> > If you never (setf (slot-value some-object 'some-attribute) nil)
> > after this some-attribute is no more useful, it won't be garbage
> > collected before some-object, ie. perhaps never.
>
> Are you advocating deallocating memory on a data memeber by data member
> basis? That seems very odd to me. If you are talking about the object that
> used to be referenced by by (slot-value some-object 'some-attribute) then
> you are mistaken that it will necesarily hang around for the life of
> some-object.

Commandment 7 relates to the following kind of code:

    // pseudo code: I've not programmed in C++ for several years.
    class example {
    protected:
        char* data;
    public:
        virtual example(void){data=0;}
        virtual void setData(const char* newData){
            dataNoMoreUseful();
            data=newstr(newData);}
        virtual const char* getData(void){
            return(data);}
        virtual void dataNoMoreUseful(void){
            if(data!=0){free(data);}}
        virtual void workWithData(void){
            doSomethingUsefulWith(data);}}

    {example* ex=new example;
        ex->setData(a_pointer_to_a_3_GB_memory_block).
        ex->workWithData();
        ex->dataNoMoreUseful();
        doSomeThingElse();
        return(ex->getData();)} ==> A "dangling" pointer!

In C/C++, the problem appears when you use getData() the second time:
you may get SIGSEGV in the best case, or invalid data at worse.

In CLOS, you'd have this kind of code:

    (defclass example
        ((data :accessor data))
        (:documentation "For the sake of the argument."))
    (defmethod set-data ((self example) new-data) (setf (data self) new-data))
    (defmethod get-data ((self example)) (data self))
    (defmethod work-with-data ((self example))
       (do-something-useful-with (data self)))

So the question is how do you implement data-no-more-useful:

    (defmethod data-no-more-useful ((self example)))

is incorrect.

    (defmethod data-no-more-useful ((self example)) (setf (data self) nil))
    
Is correct, and is what this Seventh Commandment predicates, when
tranlated to CLOS.

    (let ((ex (make-instance 'example)))
        (set-data ex (get-some-3GB-big-data))
        (work-with-data ex)
        (data-no-more-useful ex)
        (do-something-else) ==> With the incorrect implementation,
                                you get out-of-memory errors.

        (get-data ex)) ==> With the incorrect implementation, you get
                           3GB worth of data, and no problem
                           (contrarily to C/C++) ;
                           With the correct implementation, you get
                           NIL and 3GB free memory once the GC runs.

You can have memory leaks even in Lisp with a garbage collector, in
the sence that data that is no more useful may be kept in some obscure
place somewhere.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we.