Page 2 of 2

Re: pushing links, not elements

Posted: Sat Mar 07, 2009 8:28 pm
by qbg
Jasper wrote:Cool, didn't think of that, how does that compare performance-wise? You happen to know how loop/dolist do references?
Well, it would have to allocate a closure upon creation, and do a function call and comparison for reading/setting, instead of just setting a memory location like you would with a lexical variable. DOLIST and LOOP create lexical bindings (just look at their macroexpansion), so there is nothing special going on there.
Btw greatly miss with-gensyms, it should be in standard library, imo. (Same as a lot more macros/functions.)
I believe Alexandria has with-gensyms and a bunch of other great stuff.

Re: pushing links, not elements

Posted: Sun Mar 08, 2009 7:11 am
by Jasper
Of course, i just made a file with the macros i need.(But prob with posting them online is that other people don't have them) I will certainly check it out, should be interesting how my (rather small) set of macro/functions compare to those of alexandria.

Update: Ok, alexandria looks good, why (defmacro with-gensyms (names &body forms).. vs the imo more representative (defmacro with-gensyms ((&rest names) &body forms)).. It doesnt accept lone symbols here either. (Being a little pedantic here)

Also, i can see if-let and when-let, it seems like a good idea. I used single variables and if-with, when-with, case-with, *-let does not extend to case-let well, though. I don't see anything like

Code: Select all

(defmacro if-with (var cond if-t &optional (if-f nil)) `(let ((,var ,cond)) (if ,var ,if-t ,if-f)))
(defmacro if-use (cond &optional if-false) (with-gensyms (var) `(if-with,var ,cond ,var ,if-false)))
(defmacro setf- (operator set &rest args) `(setf ,set (,operator ,set ,@args)))
And anything like and*, or*, where order of execution is assured? I guess those last three are inherently not functional, maybe we should avoid them. I do use them though :-/, should write functionally more, although function callbacks seem like a good idea in many cases.

Lastly, is there an argumentize-list ((&rest arguments) list &body body) macro? Here it makes and sets variables based on arguments, which are just like that defmacros. I made one but currently it can't make keywords from symbols, and &optional doesn't work. I am planning to make a better version; though, working on another thing, i need to parse the arguments anyway.

Re: pushing links, not elements

Posted: Sun Mar 08, 2009 10:37 am
by qbg
Jasper wrote: Lastly, is there an argumentize-list ((&rest arguments) list &body body) macro? Here it makes and sets variables based on arguments, which are just like that defmacros.
You mean like DESTRUCTURING-BIND?

Re: pushing links, not elements

Posted: Sun Mar 08, 2009 11:04 am
by gugamilare
Jasper wrote:Also, i can see if-let and when-let, it seems like a good idea. I used single variables and if-with, when-with, case-with, *-let does not extend to case-let well, though. I don't see anything like

Code: Select all

(defmacro if-with (var cond if-t &optional (if-f nil)) `(let ((,var ,cond)) (if ,var ,if-t ,if-f)))
(defmacro if-use (cond &optional if-false) (with-gensyms (var) `(if-with,var ,cond ,var ,if-false)))
(defmacro setf- (operator set &rest args) `(setf ,set (,operator ,set ,@args)))
Try http://common-lisp.net/project/anaphora/. It is a pretty good library in this sense. ;)
Jasper wrote:Lastly, is there an argumentize-list ((&rest arguments) list &body body) macro? Here it makes and sets variables based on arguments, which are just like that defmacros. I made one but currently it can't make keywords from symbols, and &optional doesn't work. I am planning to make a better version; though, working on another thing, i need to parse the arguments anyway.
As said before, try destructuring-bind. It is ANSI, don't need a library.

Re: pushing links, not elements

Posted: Tue Mar 17, 2009 4:58 am
by Jasper
I just remembered why i associated using lists as reference as causing trouble, If x is a variable, (list x) does not behave as a reference to x, since x is passed by value to list.

Code: Select all

(let ((x 1))
  (flet ((set-two (ref) (setf (car ref) 2)))
    (set-two (list x)))
  x)  => 1

Re: pushing links, not elements

Posted: Tue Mar 17, 2009 8:53 am
by gugamilare
Jasper wrote:I just remembered why i associated using lists as reference as causing trouble, If x is a variable, (list x) does not behave as a reference to x, since x is passed by value to list.

Code: Select all

(let ((x 1))
  (flet ((set-two (ref) (setf (car ref) 2)))
    (set-two (list x)))
  x)  => 1
But here comes a distinction. I didn't exactly say that the car of a cons is a reference. I said that the cons itself is a reference (actually, two references, one for its car and the other for its cdr). Numbers (yes, including big integers), characters and symbols (including keywords) - am I forgetting something? - are never references in lisp because they are immediate values (or at least treated as such). Actually, in your example, if x were a list, the same would happen, although the car of (list x) is a reference.
The problem is that the reference is always to the value itself, not to the address of the value.

It looks like to me that you came from imperative languages, and that is the reason you are confused. Let me try to explain this in C world.
ECL is implemented more or less the following way: there is a type definition named cl_object which is essentially a union type. It can represent an integer (I mean the C int) or a pointer. The lower two bits of the some cl_object is a tag which tells what kind of object it represents: It can be a fixnum, a character, a pointer or an internal constant (I would guess, T, NIL and some others).

When the lower 2 tags say that the element is a fixnum, to obtain the fixnum, you shift the cl_object to the right by 2. Like this:

Code: Select all

cl_object obj;

if FIXNUMP(obj) // this checks the two lower tags and tell if they match the fixnum tag
  return ((int) obj) >> 2;
else
  die("obj is not an integer");
You obtain the character the exact same way, shifting the object by two. If the element is a list, this is what it does to obtain the car and cdr:

Code: Select all

cl_object obj, car, cdr;
ecl_cons *cons;

if CONSP(obj) {
  cons = (ecl_cons *) ( ((int) obj) >> 2);
  car = cons->cons.car;
  cdr = cons->cons.cdr;
}
else
  die("obj is not a cons");
Well, at least for me, now it is clear why (setf (car (list x)) 2) does not affect x. You will have x declared as

Code: Select all

int x;
and set (list x)'s car with something like

Code: Select all

(list_x -> cons.car) = 2;
, which would not affect x. But, in the following example:

Code: Select all

(let ((x (list 1)))
  (setf (car (car (list x))) 2)
  x) => (2)
You would have:

Code: Select all

ecl_cons* x, car_list_x;

// some other declarations and evaluations which are not so important.

_car_list_x = list_x -> cons.car;
car_list_x = (ecl_cons *) (_car_list_x >> 2); // don't mind this line, it just fetches the real car as a cons object.

// right now car_list_x is pointing to the same place that x is pointing.
// It is just like you had evaluated car_list_x = x.
// Therefore, the next line will change the car of x.

(car_list_x -> cons.car) = 2;
This is just an example, you should never use this stuff explicitly, but instead you should use the macros ECL's provides if you want to do this. But I hope this is useful for understanding the behavior of Lisp itself.
qbg wrote:
Jasper wrote:Cool, didn't think of that, how does that compare performance-wise? You happen to know how loop/dolist do references?
Well, it would have to allocate a closure upon creation, and do a function call and comparison for reading/setting, instead of just setting a memory location like you would with a lexical variable. DOLIST and LOOP create lexical bindings (just look at their macroexpansion), so there is nothing special going on there.
I have some pain in my stomach whenever I create function just to return some value when it just won't compute anything. Some simple tests in SBCL makes me believe using functions instead of structures gives about 40% of time impact. But using functions here make the place to be generalizable - with a function, you can create a reference to a field of a class - and changing the value "referenced" by the function would be able to change the value of the slot.

If I were implementing this, I would create the structure, and make the accessor "&" (or "deref") to work with both versions, just in case.