From: Onay Urfalioglu
Subject: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <42f908fd$0$6982$9b4e6d93@newsread2.arcor-online.net>
* Could someone please explain this behaviour for a newbie:

(dotimes (i0 3) 
    (setf l (list 1 2)) 
    (format t "l1:~a~%" l) 
    (setf (second l) 10) 
    (format t "l2:~a~%" l))

yields:

l1:(1 2)
l2:(10 2)
l1:(1 2)
l2:(10 2)
l1:(1 2)
l2:(10 2)                                                                                                                                                                 

and i understand this !
Now check this out:

(dotimes (i0 3) 
    (setf l (list #(1) #(2))) 
    (format t "l1:~a~%" l) 
    (setf (aref (second l) 0) 10) 
    (format t "l2:~a~%" l))

yields:

l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
l2:(#(1) #(10))
l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
l2:(#(1) #(10))

I can force the 'expected' behaviour by replacing 

(setf l (list #(1) #(2))) 

with

(setf l (generate-list))    

where generate-list is:

(defun generate-list ()
    (list  #(1) #(2)))

thx
-- 
(+::+) oni (+::+)
(I already try to be as amusing as possible, my master!)

From: ······@gmail.com
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <1123618931.777163.149390@o13g2000cwo.googlegroups.com>
Onay Urfalioglu wrote:
> * Could someone please explain this behaviour for a newbie:
>
> (dotimes (i0 3)
>     (setf l (list 1 2))
>     (format t "l1:~a~%" l)
>     (setf (second l) 10)
>     (format t "l2:~a~%" l))
>
> yields:
>
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)
>
> and i understand this !
> Now check this out:
>
> (dotimes (i0 3)
>     (setf l (list #(1) #(2)))
>     (format t "l1:~a~%" l)
>     (setf (aref (second l) 0) 10)
>     (format t "l2:~a~%" l))
>
> yields:
>
> l1:(#(1) #(2))
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))
>
> I can force the 'expected' behaviour by replacing
>
> (setf l (list #(1) #(2)))
>
> with
>
> (setf l (generate-list))
>
> where generate-list is:
>
> (defun generate-list ()
>     (list  #(1) #(2)))

In the expresson (list #(1) #(2)) the #(1) and #(2) are literal
vectors.  They are not newly allocated each time through the loop.
It's sort of like this:

int v1[] = { 1 };
int v2[] = { 2 };

for (int i0 = 0; i0 < 3; ++i0) {
  int[][] l = { v1, v2 };
  ...
  l[1][0] = 10;
}

What you want is:

(list (vector 1) (vector 2))

In that expression the vectors are newly allocated each time through
the loop and it will behave as you expect.

This is a parallel to the difference between '(1) and (list 1).  '(1)
is a list literal.  Modifying it anywhere at anytime will modify it for
all time.  (list 1) creates a fresh list each time it is evaluated.

Hope that helps.

Justin Dubs
From: Onay Urfalioglu
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <42f91b1e$0$11752$9b4e6d93@newsread4.arcor-online.net>
······@gmail.com wrote:

> In the expresson (list #(1) #(2)) the #(1) and #(2) are literal
> vectors.  They are not newly allocated each time through the loop.
> It's sort of like this:
> 
> int v1[] = { 1 };
> int v2[] = { 2 };
> 
> for (int i0 = 0; i0 < 3; ++i0) {
>   int[][] l = { v1, v2 };
>   ...
>   l[1][0] = 10;
> }
> 
> What you want is:
> 
> (list (vector 1) (vector 2))
> 
> In that expression the vectors are newly allocated each time through
> the loop and it will behave as you expect.
> 
> This is a parallel to the difference between '(1) and (list 1).  '(1)
> is a list literal.  Modifying it anywhere at anytime will modify it for
> all time.  (list 1) creates a fresh list each time it is evaluated.
> 
> Hope that helps.
> 
> Justin Dubs

Yes! Thank you very much.
So, as i understand the generation of a vector via a #(...) inside the loop
has an effect of 'being' outside the loop. What is the reason behind this
scope-rule?

thx
-- 
(+::+) oni (+::+)
(I already try to be as amusing as possible, my master!)
From: drewc
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <O89Ke.160416$%K2.40888@pd7tw1no>
Onay Urfalioglu wrote:
> ······@gmail.com wrote:
> 
> 
>>In the expresson (list #(1) #(2)) the #(1) and #(2) are literal
>>vectors.  They are not newly allocated each time through the loop.
>>It's sort of like this:
>>
>>int v1[] = { 1 };
>>int v2[] = { 2 };
>>
>>for (int i0 = 0; i0 < 3; ++i0) {
>>  int[][] l = { v1, v2 };
>>  ...
>>  l[1][0] = 10;
>>}
>>
>>What you want is:
>>
>>(list (vector 1) (vector 2))
>>
>>In that expression the vectors are newly allocated each time through
>>the loop and it will behave as you expect.
>>
>>This is a parallel to the difference between '(1) and (list 1).  '(1)
>>is a list literal.  Modifying it anywhere at anytime will modify it for
>>all time.  (list 1) creates a fresh list each time it is evaluated.
>>
>>Hope that helps.
>>
>>Justin Dubs
> 
> 
> Yes! Thank you very much.
> So, as i understand the generation of a vector via a #(...) inside the loop
> has an effect of 'being' outside the loop. What is the reason behind this
> scope-rule?

There is not scope rule involved here. Your original code was not valid, 
and while "constant collapse" explains the behavior you are seeing, an 
implementation would be free to do whatever it wanted, including crash, 
when it encounters your code.

basically, when the compiler sees your '#(2)' vector, it knows that it 
is a constant, so rather than create a new vector each time through your 
loop, it simply re-uses the one it had already created. Since you are 
(illegally) mutating that vector, when the compiler re-uses it, it's 
getting the modified vector.

to simplify, what you doing in your SETF amounts to something like 
(setf 1 2) ,which is nonsense, right? but if your compiler allowed you 
to do it, everywhere you used the literal digit '1', you'd infact be 
pointing to a value of 2. the compiler does not create a new copy of '1' 
every time it sees it, as 1 is a constant, so it reuses the value. (this 
is vastly simplified and entirely unfounded in fact, so take it with a 
grain of salt).

Hope that helps,

-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: Pascal Bourguignon
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <87hddyn6ez.fsf@thalassa.informatimago.com>
drewc <·····@rift.com> writes:
> basically, when the compiler sees your '#(2)' vector, it knows that it
> is a constant, 

The complier doesn't see any '#(2)'.  It cannot know it's an immutable
or literal vector, because there's no introspective notion of
immutable or literal in Common Lisp.  It just sees _a_ vector.  
It would behave the same have you written:

(setf l (list #.(make-array 1 :initial-contents '(1))
              #.(make-array 1 :initial-contents (list (+ 1 1)))))

That is, when you write #(2) you explicitely ask the reader to create
a vector _at_ _read_ _time_.  Thereafter it stays always the same vector.

-- 
"Klingon function calls do not have "parameters" -- they have
"arguments" and they ALWAYS WIN THEM."
From: drewc
Subject: Read Time Evaluation in other languages ? (was Re: surprizing behaviour (at least for a c/c++ veteran))
Date: 
Message-ID: <XyaKe.162019$%K2.137151@pd7tw1no>
Pascal Bourguignon wrote:
> drewc <·····@rift.com> writes:
> 
>>basically, when the compiler sees your '#(2)' vector, it knows that it
>>is a constant, 
> 
> 
> The complier doesn't see any '#(2)'.  It cannot know it's an immutable
> or literal vector, because there's no introspective notion of
> immutable or literal in Common Lisp.  It just sees _a_ vector.  
> It would behave the same have you written:
> 
> (setf l (list #.(make-array 1 :initial-contents '(1))
>               #.(make-array 1 :initial-contents (list (+ 1 1)))))
> 
> That is, when you write #(2) you explicitely ask the reader to create
> a vector _at_ _read_ _time_.  Thereafter it stays always the same vector.
> 

I over-simplified, and was using "compiler" poorly (in an honest attempt 
to get the point across to a c/c++ veteran). This is an important point, 
and i'm glad you were around to make it :), and your example in C was 
spectacular ! (i did parenthetically point out my nonsense).

You pointed out that in C the different phases of of execution are 
performed in different proccesses, and they don't have access to one 
anothers data. I'd say the latter is true of most languages, so i'm 
wondering, what other languages/programming environments are/were there 
that offer similar features?

I'm specifically thinking of read-time evaluation (as in your example 
above), and the separation of read time from compile and macroexpand 
time, with the full language facilities available. Common Lisp takes 
this to the extreme, and allows you to hook into the reader itself. Was 
this level of control available in earlier lisps? Was there ever a 
'better' reader (i'm thinking of DWIM and that sort of thing here).

Thanks for any insights :)
-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: Pascal Bourguignon
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <87slxin76h.fsf@thalassa.informatimago.com>
Onay Urfalioglu <·····@gmx.net> writes:

> * Could someone please explain this behaviour for a newbie:
>
> (dotimes (i0 3) 
>     (setf l (list 1 2)) 
>     (format t "l1:~a~%" l) 
>     (setf (second l) 10) 
>     (format t "l2:~a~%" l))
>
> yields:
>
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)


You must be lying us.  The expected results are:

[1]> (dotimes (i0 3) 
     (setf l (list 1 2)) 
     (format t "l1:~a~%" l) 
     (setf (second l) 10) 
     (format t "l2:~a~%" l))
l1:(1 2)
l2:(1 10)
l1:(1 2)
l2:(1 10)
l1:(1 2)
l2:(1 10)
NIL

Copy-and-paste have been available to the masses for 21 years!  Use it.


> and i understand this !
> Now check this out:
>
> (dotimes (i0 3) 
>     (setf l (list #(1) #(2))) 
>     (format t "l1:~a~%" l) 
>     (setf (aref (second l) 0) 10) 
>     (format t "l2:~a~%" l))
>
> yields:

>
> l1:(#(1) #(2))
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))

#(1) is a literal vector that appears in the source of the program,
exactly like the list (1 2) obtained with (setf l (quote (1 2))), or
the string "Hello".  All these literals must be _treated_ as
immutable.  It's undefined what happens when a program tries to modify
them. See CLHS 3.7.1 Modification of Literal Objects


Another way to see the "unexpected behaviour" is this:

[5]> (defun test () (dotimes (i0 3) 
                    (setf l (list #(1) #(2))) 
                    (format t "l1:~a~%" l) 
                    (setf (aref (second l) 0) 10) 
                    (format t "l2:~a~%" l)))
TEST
[6]> (fdefinition 'test)
#<FUNCTION TEST NIL (DECLARE (SYSTEM::IN-DEFUN TEST))
  (BLOCK TEST
   (DOTIMES (I0 3) (SETF L (LIST #(1) #(2))) (FORMAT T "l1:~a~%" L)
    (SETF (AREF (SECOND L) 0) 10) (FORMAT T "l2:~a~%" L)))>
[7]> (test)
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
NIL
[8]> (fdefinition 'test)
#<FUNCTION TEST NIL (DECLARE (SYSTEM::IN-DEFUN TEST))
  (BLOCK TEST
   (DOTIMES (I0 3) (SETF L (LIST #(1) #(10))) (FORMAT T "l1:~a~%" L)
    (SETF (AREF (SECOND L) 0) 10) (FORMAT T "l2:~a~%" L)))>

You've been modifying the _source_ of the program!


> I can force the 'expected' behaviour by replacing 
>
> (setf l (list #(1) #(2))) 
>
> with
>
> (setf l (generate-list))    
>
> where generate-list is:
>
> (defun generate-list ()
>     (list  #(1) #(2)))

I doubt it:

[9]> (defun generate-list ()  (list  #(1) #(2)))
GENERATE-LIST
[10]> (defun test () (dotimes (i0 3) 
                    (setf l (generate-list))
                    (format t "l1:~a~%" l) 
                    (setf (aref (second l) 0) 10) 
                    (format t "l2:~a~%" l)))
TEST
[11]> (fdefinition 'generate-list)
#<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
  (BLOCK GENERATE-LIST (LIST #(1) #(2)))>
[12]> (test)
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
NIL
[13]> (fdefinition 'generate-list)
#<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
  (BLOCK GENERATE-LIST (LIST #(1) #(10)))>


The correct way is to create a new array, _at_ _run_ _time_ instead of
at read time:


[19]> (dotimes (i0 3) 
                    (setf l (list (make-array 1 :initial-contents '(1))
                                  (make-array 1 :initial-contents '(2))))
                    (format t "l1:~a~%" l) 
                    (setf (aref (second l) 0) 10) 
                    (format t "l2:~a~%" l))
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(2))
l2:(#(1) #(10))
NIL




In lisp, read, macroexpansion, compilation and run times may occur in
the same image, with data from the various phases available in the
other phases.

In C, there's no read time (AFAIK), macroexpansion time occurs in the
cpp process, compilation time occurs in the cc1 proecss, and run time
occurs in the program process, and data from one phase is not avaiable
to the other phase.  However you can still get "surprising" results:

-*- mode: compilation; default-directory: "/tmp/" -*-
make -k CFLAGS=-g a;cat a.c;./a
make: `a' is up to date.
#include <stdio.h>
    typedef int ivec1[1];
    ivec1 list[2]={{1},{2}};
    int main(void){
        int i;
        for(i=0;i<3;i++){
            ivec1* l=list;
            printf("l1=%d %d\n",l[0][0],l[0][1]);
            l[0][1]=10;
            printf("l2=%d %d\n",l[0][0],l[0][1]);}
        return(0);}
l1=1 2
l2=1 10
l1=1 10
l2=1 10
l1=1 10
l2=1 10

Compilation finished at Tue Aug  9 23:09:16


-- 
"A TRUE Klingon warrior does not comment his code!"
From: Onay Urfalioglu
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <42f92e55$0$11756$9b4e6d93@newsread4.arcor-online.net>
Pascal Bourguignon wrote:
> #(1) is a literal vector that appears in the source of the program,
> exactly like the list (1 2) obtained with (setf l (quote (1 2))), or
> the string "Hello".  All these literals must be _treated_ as
> immutable.  It's undefined what happens when a program tries to modify
> them. See CLHS 3.7.1 Modification of Literal Objects
> 
> 
> Another way to see the "unexpected behaviour" is this:
> 
> [5]> (defun test () (dotimes (i0 3)
>                     (setf l (list #(1) #(2)))
>                     (format t "l1:~a~%" l)
>                     (setf (aref (second l) 0) 10)
>                     (format t "l2:~a~%" l)))
> TEST
> [6]> (fdefinition 'test)
> #<FUNCTION TEST NIL (DECLARE (SYSTEM::IN-DEFUN TEST))
>   (BLOCK TEST
>    (DOTIMES (I0 3) (SETF L (LIST #(1) #(2))) (FORMAT T "l1:~a~%" L)
>     (SETF (AREF (SECOND L) 0) 10) (FORMAT T "l2:~a~%" L)))>
> [7]> (test)
> l1:(#(1) #(2))
> l2:(#(1) #(10))
> l1:(#(1) #(10))
> l2:(#(1) #(10))
> l1:(#(1) #(10))
> l2:(#(1) #(10))
> NIL
> [8]> (fdefinition 'test)
> #<FUNCTION TEST NIL (DECLARE (SYSTEM::IN-DEFUN TEST))
>   (BLOCK TEST
>    (DOTIMES (I0 3) (SETF L (LIST #(1) #(10))) (FORMAT T "l1:~a~%" L)
>     (SETF (AREF (SECOND L) 0) 10) (FORMAT T "l2:~a~%" L)))>
> 
> You've been modifying the _source_ of the program!

Oh, that's interesting !
 
>> I can force the 'expected' behaviour by replacing
>>
>> (setf l (list #(1) #(2)))
>>
>> with
>>
>> (setf l (generate-list))
>>
>> where generate-list is:
>>
>> (defun generate-list ()
>>     (list  #(1) #(2)))
> 
> I doubt it:
> 
> [9]> (defun generate-list ()  (list  #(1) #(2)))
> GENERATE-LIST
> [10]> (defun test () (dotimes (i0 3)
>                     (setf l (generate-list))
>                     (format t "l1:~a~%" l)
>                     (setf (aref (second l) 0) 10)
>                     (format t "l2:~a~%" l)))
> TEST
> [11]> (fdefinition 'generate-list)
> #<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
>   (BLOCK GENERATE-LIST (LIST #(1) #(2)))>
> [12]> (test)
> l1:(#(1) #(2))
> l2:(#(1) #(10))
> l1:(#(1) #(10))
> l2:(#(1) #(10))
> l1:(#(1) #(10))
> l2:(#(1) #(10))
> NIL
> [13]> (fdefinition 'generate-list)
> #<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
>   (BLOCK GENERATE-LIST (LIST #(1) #(10)))>

Yes, my mistake. Lisp is quite different to me, so my brain gets BBQ'ed.
 

> In lisp, read, macroexpansion, compilation and run times may occur in
> the same image, with data from the various phases available in the
> other phases.
> 
> In C, there's no read time (AFAIK), macroexpansion time occurs in the
> cpp process, compilation time occurs in the cc1 proecss, and run time
> occurs in the program process, and data from one phase is not avaiable
> to the other phase.  However you can still get "surprising" results:
> 
> -*- mode: compilation; default-directory: "/tmp/" -*-
> make -k CFLAGS=-g a;cat a.c;./a
> make: `a' is up to date.
> #include <stdio.h>
>     typedef int ivec1[1];
>     ivec1 list[2]={{1},{2}};
>     int main(void){
>         int i;
>         for(i=0;i<3;i++){
>             ivec1* l=list;
>             printf("l1=%d %d\n",l[0][0],l[0][1]);
>             l[0][1]=10;
>             printf("l2=%d %d\n",l[0][0],l[0][1]);}
>         return(0);}
> l1=1 2
> l2=1 10
> l1=1 10
> l2=1 10
> l1=1 10
> l2=1 10

That's a good example to show the difference: Here, variable 'list' is
defined globally outer the loop. 'l', though defined whithin the loop
points to 'list'. 
From this analogy, i seem to understand that in common-lisp the literal
objects are global scoped and somewhat like static variables in C ...

Thank you all very much for this insightfull information. I got it.

-- 
(+::+) oni (+::+)
(I already try to be as amusing as possible, my master!)
From: Peter Seibel
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <m2fytin0wm.fsf@gigamonkeys.com>
Onay Urfalioglu <·····@gmx.net> writes:

> From this analogy, i seem to understand that in common-lisp the literal
> objects are global scoped and somewhat like static variables in C ...
>
> Thank you all very much for this insightfull information. I got it.

Not quite. A better analogy to C is what happens if you try to modify
a string literal. (I.e. something in "...") In C this is undefined
behavior; likewise in Common Lisp, modifying literal objects is
undefined. It may "work" the way you've seen here or it may, as they
say over in comp.lang.c, cause demons to fly out your nose.

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: drewc
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <sX8Ke.161216$s54.46276@pd7tw2no>
Onay Urfalioglu wrote:
> * Could someone please explain this behaviour for a newbie:
> 
> (dotimes (i0 3) 
>     (setf l (list 1 2)) 
>     (format t "l1:~a~%" l) 
>     (setf (second l) 10) 
>     (format t "l2:~a~%" l))
> 
> yields:
> 
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)                                                                                                                                                                 
> 
> and i understand this !
> Now check this out:
> 
> (dotimes (i0 3) 
>     (setf l (list #(1) #(2))) 
>     (format t "l1:~a~%" l) 
>     (setf (aref (second l) 0) 10) 
>     (format t "l2:~a~%" l))
> 
> yields:
> 
> l1:(#(1) #(2))
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))

The problem here is that you are trying to modify a constant. #() 
creates a literal vector in the same manner that (quote foo) also 
returns a constant which should not be modified. Your implementation is 
free to 'collapse' literal constants when it sees them, and this is the 
behavior you are experiencing.

observe the following :

MAXWELL-WEB-GUI> (let ((foo 'bar))
		  #(foo))
;
; caught STYLE-WARNING:
;   The variable FOO is defined but never used.
;
; compilation unit finished
;   caught 1 STYLE-WARNING condition
#(FOO)

and compare:


MAXWELL-WEB-GUI> (let ((foo 'bar))
		  (vector foo))
#(BAR)


Use functions to create mutable objects, rather than the constants you 
are using now, and the problem will go away. It is a common mistake of 
new users to modify constant data, and IMHO the implementation should at 
least warn on this, but live and learn right?

> 
> I can force the 'expected' behaviour by replacing 
> 
> (setf l (list #(1) #(2))) 
> 
> with
> 
> (setf l (generate-list))    
> 
> where generate-list is:
> 
> (defun generate-list ()
>     (list  #(1) #(2)))

Don't do that! In this case the compiler can no longer collapse the 
contstants, so you are seeing the correct results, but your code is 
non-conforming as it is still making modifications to contstant data, 
and the nasal demons should be forthcoming any minute now.

Use VECTOR instead of #() when you wish to mutate the values.

-- 
Drew Crampsie
drewc at tech dot coop
"Never mind the bollocks -- here's the sexp's tools."
	-- Karl A. Krueger on comp.lang.lisp
From: Pascal Bourguignon
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <871x52oluw.fsf@thalassa.informatimago.com>
Onay Urfalioglu <·····@gmx.net> writes:

> * Could someone please explain this behaviour for a newbie:
>
> (dotimes (i0 3) 
>     (setf l (list 1 2)) 
>     (format t "l1:~a~%" l) 
>     (setf (second l) 10) 
>     (format t "l2:~a~%" l))
>
> yields:
>
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)
> l1:(1 2)
> l2:(10 2)


You must be lying us.  The expected results are:

[1]> (dotimes (i0 3) 
     (setf l (list 1 2)) 
     (format t "l1:~a~%" l) 
     (setf (second l) 10) 
     (format t "l2:~a~%" l))
l1:(1 2)
l2:(1 10)
l1:(1 2)
l2:(1 10)
l1:(1 2)
l2:(1 10)
NIL

Copy-and-paste have been available to the masses for 21 years!  Use it.


> and i understand this !
> Now check this out:
>
> (dotimes (i0 3) 
>     (setf l (list #(1) #(2))) 
>     (format t "l1:~a~%" l) 
>     (setf (aref (second l) 0) 10) 
>     (format t "l2:~a~%" l))
>
> yields:

>
> l1:(#(1) #(2))
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))
> l1:(#(1) #(10)) <-- unexpected. expected output: "l1:(#(1) #(2))"
> l2:(#(1) #(10))

#(1) is a literal vector that appears in the source of the program,
exactly like the list (1 2) obtained with (setf l (quote (1 2))), or
the string "Hello".  All these literals must be _treated_ as
immutable.  It's undefined what happens when a program tries to modify
them. See CLHS 3.7.1 Modification of Literal Objects


Another way to see the "unexpected behaviour" is this:

[5]> (defun test () (dotimes (i0 3) 
                    (setf l (list #(1) #(2))) 
                    (format t "l1:~a~%" l) 
                    (setf (aref (second l) 0) 10) 
                    (format t "l2:~a~%" l)))
TEST
[6]> (fdefinition 'test)
#<FUNCTION TEST NIL (DECLARE (SYSTEM::IN-DEFUN TEST))
  (BLOCK TEST
   (DOTIMES (I0 3) (SETF L (LIST #(1) #(2))) (FORMAT T "l1:~a~%" L)
    (SETF (AREF (SECOND L) 0) 10) (FORMAT T "l2:~a~%" L)))>
[7]> (test)
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
NIL
[8]> (fdefinition 'test)
#<FUNCTION TEST NIL (DECLARE (SYSTEM::IN-DEFUN TEST))
  (BLOCK TEST
   (DOTIMES (I0 3) (SETF L (LIST #(1) #(10))) (FORMAT T "l1:~a~%" L)
    (SETF (AREF (SECOND L) 0) 10) (FORMAT T "l2:~a~%" L)))>

You've been modifying the _source_ of the program!


> I can force the 'expected' behaviour by replacing 
>
> (setf l (list #(1) #(2))) 
>
> with
>
> (setf l (generate-list))    
>
> where generate-list is:
>
> (defun generate-list ()
>     (list  #(1) #(2)))

I doubt it:

[9]> (defun generate-list ()  (list  #(1) #(2)))
GENERATE-LIST
[10]> (defun test () (dotimes (i0 3) 
                    (setf l (generate-list))
                    (format t "l1:~a~%" l) 
                    (setf (aref (second l) 0) 10) 
                    (format t "l2:~a~%" l)))
TEST
[11]> (fdefinition 'generate-list)
#<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
  (BLOCK GENERATE-LIST (LIST #(1) #(2)))>
[12]> (test)
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
NIL
[13]> (fdefinition 'generate-list)
#<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
  (BLOCK GENERATE-LIST (LIST #(1) #(10)))>[9]> (defun generate-list ()  (list  #(1) #(2)))
GENERATE-LIST
[10]> (defun test () (dotimes (i0 3) 
                    (setf l (generate-list))
                    (format t "l1:~a~%" l) 
                    (setf (aref (second l) 0) 10) 
                    (format t "l2:~a~%" l)))
TEST
[11]> (fdefinition 'generate-list)
#<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
  (BLOCK GENERATE-LIST (LIST #(1) #(2)))>
[12]> (test)
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
l1:(#(1) #(10))
l2:(#(1) #(10))
NIL
[13]> (fdefinition 'generate-list)
#<FUNCTION GENERATE-LIST NIL (DECLARE (SYSTEM::IN-DEFUN GENERATE-LIST))
  (BLOCK GENERATE-LIST (LIST #(1) #(10)))>


The correct way is to create a new array, _at_ _run_ _time_ instead of
at read time:


[19]> (dotimes (i0 3) 
                    (setf l (list (make-array 1 :initial-contents '(1))
                                  (make-array 1 :initial-contents '(2))))
                    (format t "l1:~a~%" l) 
                    (setf (aref (second l) 0) 10) 
                    (format t "l2:~a~%" l))
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(2))
l2:(#(1) #(10))
l1:(#(1) #(2))
l2:(#(1) #(10))
NIL




In lisp, read, macroexpansion, compilation and run times may occur in
the same image, with data from the various phases available in the
other phases.

In C, there's no read time (AFAIK), macroexpansion time occurs in the
cpp process, compilation time occurs in the cc1 proecss, and run time
occurs in the program process, and data from one phase is not avaiable
to the other phase.  However you can still get "surprising" results:

-*- mode: compilation; default-directory: "/tmp/" -*-
make -k CFLAGS=-g a;cat a.c;./a
make: `a' is up to date.
#include <stdio.h>
    typedef int ivec1[1];
    ivec1 list[2]={{1},{2}};
    int main(void){
        int i;
        for(i=0;i<3;i++){
            ivec1* l=list;
            printf("l1=%d %d\n",l[0][0],l[0][1]);
            l[0][1]=10;
            printf("l2=%d %d\n",l[0][0],l[0][1]);}
        return(0);}
l1=1 2
l2=1 10
l1=1 10
l2=1 10
l1=1 10
l2=1 10

Compilation finished at Tue Aug  9 23:09:16


-- 
"A TRUE Klingon warrior does not comment his code!"
From: Christopher C. Stacy
Subject: Re: surprizing behaviour (at least for a c/c++ veteran)
Date: 
Message-ID: <uslxi31tb.fsf@news.dtpq.com>
Onay Urfalioglu <·····@gmx.net> writes:
> (dotimes (i0 3) 
>     (setf l (list #(1) #(2))) 
>     (format t "l1:~a~%" l) 
>     (setf (aref (second l) 0) 10) 
>     (format t "l2:~a~%" l))

As other have pointed out, you are modifying a literal.

   -----
   3.7.1 Modification of Literal Objects

   The consequences are undefined if literal objects 
   are destructively modified. For this purpose, 
   the following operations are considered destructive:
   [...]

   literal adj. (of an object) referenced directly in a
   program rather than being computed by the program;
   that is, appearing as data in a quote form, or, if the
   object is a self-evaluating object, appearing as unquoted data.
   In the form (cons "one" '("two")), the expressions "one", ("two"), 
   and "two" are literal objects.''
   -----

But I don't know why the compiler could not warn you about that.
It can see where L is being set to a list containing literals, 
and then nothing happens to L, and then a reference that is constantly
to one of those literals, and that it's trying to modify it.

This is a frequently asked question, in one form or another.

Sometimes this kind of code will work as newbies expect it to.
I'm not sure that's doing the user a big favor.

Why is it hard for the compiler to warn the user, 
at least in the cases that it could obviously know
that it's going to lose?  (And maybe in cases where
it might win, but where it's still legally undefined?)