freenode/#lisp - IRC Chatlog
Search
22:14:40
makomo
http://clhs.lisp.se/Body/05_ac.htm lists a couple of "read-modify-write" macros, describes their general form as "(operator preceding-form* place following-form*)" and defines the order of evaluation of their subforms. what i'm interested in particular is points (3) and (4) -- do you think (3) (4) is a better order than (4) (3)?
22:14:42
makomo
personally i think the order (3) (4) is weird, because one would expect the value to be read (point (4)) right after the place's subforms are evaluated (before (3)), and not at the end of evaluating "following-forms" (after (3)).
22:14:49
makomo
even define-modify-macro defines macros that work this way because their general form is (disregarding the multiple evaluation problem of the place's subforms) "(setf ,reference (function ,reference ,arg1 ,arg2 ...))". the order here is (2) (4) (3).
22:14:52
makomo
the page above doesn't even list any macros that have "following-forms" and that use (3) (4) and i don't know of any examples off the top of my head. are there even any?
22:14:55
makomo
this question of style might vary from macro to macro, but as a concrete example, what order would you expect from the macro "_f" given by https://i.imgur.com/ucUEOP0.png? disregard the actual implementation and try to only look at the call to the macro. here are the two implementations but with the modification that "op" isn't a symbol but a form that is evaluated to get a function:
22:24:06
drmeister
How would I get a slot-definition given the slot name and the class of an object?
23:29:32
pjb
makomo: I don't understand it. (let ((v (vector 0 0 0 0 0 0)) (i 0)) (incf (aref v (incf i)) (incf i)) v) #| --> #(0 2 0 0 0 0) |# (in all implementations).
23:33:23
makomo
pjb: the analogy in your example would be "when is the place given by (aref v (incf i)) read?
23:33:54
makomo
is it right after all of the subforms of AREF are evaluated, or only when all of the subforms of the outer INCF have been evaluated?
23:34:17
makomo
you have nothing in your example that would affect the AREF place though, so it's not a very good example
23:35:02
pjb
How would you have anything affecting the operator of the place, when the purpose of define-modify-macro (and other setf-expanders), is to make sure that the subforms are evaluated once for all?
23:36:03
pjb
(let ((v (vector 0 0 0 0 0 0)) (i 0)) (incf (aref v (incf i)) (progn (setf (aref v 1) 42) (incf i))) v) #| --> #(0 44 0 0 0 0) |#
23:37:32
makomo
pjb: from what i can see in 5.1.3 that's not correct. preceding-forms are handled in (1). the subforms of the place are handled in (2)
23:37:59
makomo
i get that incf & co. work however they work, but i'm wondering about defining your own place-based macros
23:38:11
pjb
makomo: I don't know if (4) (3) would be better than (3) (4), but the later order, as specified, seems more logical to me.
23:38:52
makomo
pjb: none of the macros listed in the figure ever use (3), because they don't have any following-forms
23:39:09
pjb
Ok, they evaluate the subforms of the place in (2). I don't know what the preceding-forms in (1) are.
23:40:18
makomo
but no place-based macro has a following-form, so i don't have any examples (3) (4) vs. (4) (3)
23:41:12
pjb
with (3) (4) we get: (let ((v (vector 0 0 0 0 0 0)) (i 0)) (incf (aref v (incf i)) (progn (setf (aref v 1) 42) (incf i))) v) #| --> #(0 44 0 0 0 0) |#
23:41:23
pjb
with (4) (3) we'd have: (let ((v (vector 0 0 0 0 0 0)) (i 0)) (incf (aref v (incf i)) (progn (setf (aref v 1) 42) (incf i))) v) #| --> #(0 2 0 0 0 0) |#
23:41:45
pjb
so the setf would be inoperative. The first option gives us more semantic alternatives.
23:42:58
pjb
Also, 5.1.3 specifies two separate phases: evaluation of the forms an subforms. Then read-compute-storee. Both phases can be performed thus easily by different parts: a macro for the eval, a function for the update.
23:43:43
pjb
Now, who will write a modify macro where there are both preceding-forms and following-forms?
23:46:06
makomo
idk, in this case i think i would prefer (4) (3), because _f almost looks like a normal function call, except that is modifies its first argument (which is given as a place)
23:46:19
pjb
Yes. Nice example. Indeed, if you want to be consistent with 5.1.3, you will have to use _f34.
23:47:09
pjb
Well, there's always the left-to-right order of evaluation. At least, _f34 respects that.
23:48:43
makomo
hm what do you mean? don't they both respect the left-to-right order? (i'm not treating reading the place as an evaluation btw. i'm only taking into account the subforms of the macro and the place)
23:48:46
pjb
Sorry, I've got a harder time understanding what the values of (car yo) will be in the various subexpressions of _f43.
23:50:52
makomo
i agree that both examples are pretty horrible :-D, but f43 might be a little easier to think about
23:52:26
pjb
Are you sure such a macro would be useful? Instead, you could write a macro to generate a bunch of define-modify-macro forms for all the functions you need?
23:53:09
pjb
Will you have things like: (_f43 (case (random 3) (0 (function sin)) (1 (function cos)) (3 (function atan))) (car yo)) ?
23:53:33
makomo
hm well, i'm not sure yet, but wouldn't that be like defining lots of little useless functions instead of just using LAMBDA?
23:54:07
makomo
i.e. _f allows you to use a function "on-the-spot", without having to define a one-off read-compute-update macro for it
23:54:29
pjb
The question is whether the functions will always be known at compilation time, or whether they will be decided at run-time. In the former case, I don't think a macro like _f43 or _f34 is justified.
23:55:23
makomo
btw, from what i can see, (4) (3) is how it's implemented in On Lisp https://i.imgur.com/LXmuDG8.png
23:56:55
pjb
So for consistency you could still want to use _f34. Just so (incf …) and (_f34 '+ …) give the same results.
23:59:10
makomo
i guess that's a good point. i'm not sure whether that or "it almost looks like a function call and i expect left-to-right evaluation" is more compelling
0:00:32
makomo
an unrelated but interesting thing is that, theoretically, getting a place's value could yield different results depending on how you get the value: (1) by treating the place as a form or (2) by using code generated by get-setf-expansion
0:01:16
makomo
because (2) might arrange a different order of evaluation that what you would get from (1) by relying on normal left-to-right rules
0:03:13
pjb
You mean things that you can put as first argument of setf, but couldn't be used to get an object? Kind of write-only forms?
0:03:33
pjb
I guess you can do it easily with (defun (setf foo) (new-value where) …) without a defun foo.
0:07:28
pjb
There's very few write-only stuff in CL. IIRC, there must be one or two, but not with accessors.
0:54:04
aeth
I have an incredibly complicated loop (yes, more than the one from yesterday) because it collects twice, appends twice, and also generates an integer value that's used. https://gitlab.com/zombie-raptor/zombie-raptor/blob/master/entity/entity.lisp#L164-225
0:55:56
pjb
(loop for i in (loop repeat 4 collect 42) for j from 0 collect (loop for k below j collect (list i k))) #| --> (nil ((42 0)) ((42 0) (42 1)) ((42 0) (42 1) (42 2))) |#
0:56:48
pjb
aeth: a function must do a single thing. Unless you're looping over a multidimensional array, there should be as single loop per function.
0:57:20
aeth
Oh I did find a way to fix my emacs's indentation and clean up yesterday's loop a bit. https://gitlab.com/zombie-raptor/zombie-raptor/blob/b10b0ce00d4c27ec20860991907baae11e188040/util/array.lisp#L113-151
0:59:34
aeth
pjb: yes, but the five different loops (only two were actually LOOPs) were harder to follow because there were a bunch of things I combined when I combined them
1:00:23
aeth
e.g. now I collect a gensym that's generated each iteration into a list of gensyms, instead of generating that list of gensyms and using it both in the loops and in the macro, which then requires MAPCARing over two lists, which is more complicated than over one
1:01:02
aeth
And, yes, I use ? here instead of -p because it's a query mini-language (as the name suggests)
1:01:17
aeth
it queries a big, complicated data structure and symbol-macrolets away the implementation details to the variables you provide
1:02:03
aeth
pjb: I had it in 5 separate loops (well two were LOOPs and two were MAPCAR and one was REDUCE) and I couldn't read it
1:02:52
aeth
I could possibly spin out the boolean-set number generation into its own function because it isn't heavily tied to the rest.
1:06:05
aeth
Any mutation that changes the visuals of the engine must make changed? T and if you need to access the value of changed (but not set it!) you use LET-CHANGED? to get the result of `(= 1 ,%changed?)
1:09:10
aeth
pjb: okay, I fixed the accessor name and simplified the destructuring. https://gitlab.com/zombie-raptor/zombie-raptor/blob/a6b92a4ed50ad49cf709604bd15def274a3b97f5/entity/entity.lisp#L166-224
1:18:04
aeth
I think the big loop is more readable if I can replace the append, with the possible exception of the boolean-set part, which I could just spin off into a function and put at the top of the loop in another :with
1:18:39
pjb
But it may (reload it I missed the bindings function) be easier to read, since the naming of the important data is clearer in the let* form, and we can abstract away parts such as the bindings function (we could do the same for array-bindings and boolean-set); those functions could be reusable, etc.
1:19:31
aeth
pjb: Well, in one intermediate step I had the loop return multiple values into a multiple-value-bind. It seemed kind of pointless, *but* if I moved the loop into a separate function, it could be cleaner
1:19:45
pjb
The thing is that by writing separate loops, you can extract the computing of each list in a separate function.
1:25:44
pjb
Actually, I've got a backup of lisppaste with almost all of them. And scanning the irc logs, I could recover almost all the other urls…
1:26:20
aeth
pjb: How about this for now? https://gitlab.com/zombie-raptor/zombie-raptor/blob/69615bd70dae5627096702ee0a7e2a3564fd07de/entity/entity.lisp#L164-228
1:28:03
aeth
array-bindings is the non-trivial part I have to figure out how to deal with (whether in its own loop or not).
1:48:43
aeth
With the reduce in its own function. https://gitlab.com/zombie-raptor/zombie-raptor/blob/4d0f226962d3c699ebc3fe3f208b5fd7b5e09bfa/entity/entity.lisp#L166-235
4:15:12
drmeister
Say I have a vector of some number type and I want to convert it to a vector of single-floats - I have to convert the elements one by one - right?
5:32:43
beach
SaganMan: I am fine, thank you. I have been making excellent progress on bootstrapping the past few days. What about you?
5:34:38
SaganMan
beach: I'm in my family business. It's real estate and construction. We take land for development and construct apartments.
5:37:38
SaganMan
beach: We don't usually do projects on that grand scale. That is the biggest investment in mine and my father's life. It looks impressive but it's great risk and stress.
6:20:48
no-defun-allowed
well my ffmpeg interface is still shitslow at 4fps but the videos aren't broken now