freenode/#lisp - IRC Chatlog
Search
12:47:22
xificurC
I just got a question from colleagues what does "prog" stand for in progn or prog1, anyone knows?
12:50:14
makomo
perhaps the sequence of forms that you give to prog can be thought of as a "program", i.e. a sequence steps to execute in that particular order
12:51:52
xificurC
which is no different to normal code :) And program usually refers to something bigger, this is just a random block of code grouped together
12:55:37
Xach_
at ilc guy steele and someone else made a few jokes about the announcement of PROG back in the day. apparently it was pitched as making it possible to write programs in lisp.
13:02:52
nowhere_man
Has anyone written a macro that contains a defmacro? I'm really struggling here.
13:05:23
nowhere_man
Writing the "first-level" macro is very easy: (defmacro {lazy} ((&rest args) &body body) `(make-instance 'lazy-function :fun (lambda ,args ,@body)))
13:05:46
nowhere_man
Now I'd like to write several identical macros with the name and class changing
13:06:07
nowhere_man
shka: they are straight up mysterious, I've been playing with them for some time now
13:07:49
nowhere_man
and in this case, I would have liked to understand how I can manage to pass the class as a symbol
13:10:02
xificurC
(defmacro uber-lazy (mname class) `(defmacro ,mname (args &body body) `(make-instance ',,class :fun (lambda ,args ,@body))))
13:10:42
xificurC
(defmacro uber-lazy (mname class) `(defmacro ,,mname (args &body body) `(make-instance ',,class :fun (lambda ,args ,@body))))
13:13:47
nowhere_man
I'd like to be able to do both (make-function-maker {lazy} 'lazy-function) and (make-function-maker {other} (class-of something))
13:15:38
nowhere_man
It works like this with just class names: https://plaster.tymoon.eu/view/805#805
13:16:34
makomo
i was just about to ask, wouldn't that require for this defmacro to not be at the top-level
13:21:04
Bike
I mean, and you'll have to do (make-function-maker lazy 'lazy-function) instead of just (make-function-maker lazy lazy-function), of course.
14:26:59
makomo
the ",,@" in once-only still puzzles me, because i can't seem to get at the observer behavior using the formal definition
14:27:30
makomo
the rightmost ",@" splices in some list, and the leftmost "," then evaluates *every* element of the list
14:38:33
pjb
makomo: I beg to differ: (let ((l '((+ 1 2) (- 1 2) (* 3 4)))) ``(list ,,@l)) #| --> (list* 'list (list (+ 1 2) (- 1 2) (* 3 4))) |#
14:40:09
pjb
What matters, is that the inner , matches the inner `: (let ((out '((+ 1 2) (- 1 2) (* 3 4)))) `(let ((in '((/ 3 4) (/ 1 2)))) `(list ,@in ,,@out)))
14:40:15
pjb
(let ((out '((+ 1 2) (- 1 2) (* 3 4)))) `(let ((in '((/ 3 4) (/ 1 2)))) `(list ,@in ,,@out))) #| --> (let ((in '((/ 3 4) (/ 1 2)))) (list* 'list (append in (list (+ 1 2) (- 1 2) (* 3 4))))) |#
14:40:29
pjb
(eval (let ((out '((+ 1 2) (- 1 2) (* 3 4)))) `(let ((in '((/ 3 4) (/ 1 2)))) `(list ,@in ,,@out)))) #| --> (list (/ 3 4) (/ 1 2) 3 -1 12) |#
14:40:38
pjb
(eval (eval (let ((out '((+ 1 2) (- 1 2) (* 3 4)))) `(let ((in '((/ 3 4) (/ 1 2)))) `(list ,@in ,,@out))))) #| --> (3/4 1/2 3 -1 12) |#
14:41:34
pjb
You need eval, or a macro to evaluate the form: (eval (let ((l '((+ 1 2) (- 1 2) (* 3 4)))) ``(list ,,@l)) ) #| --> (list 3 -1 12) |#
14:41:53
pjb
and since we have two ``, we need two evals or two macros: (eval (eval (let ((l '((+ 1 2) (- 1 2) (* 3 4)))) ``(list ,,@l)) )) #| --> (3 -1 12) |#
14:45:42
makomo
pjb: isn't what i said true? your list is ((+ 1 2) (- 1 2) ...), and the result of evaluating the doubly nested backquote form twice is '(list 3 -1 12)
14:46:11
beach
jmercouris: The word is more commonly used to mean a dummy element that is part of the representation of a data structure, but that is not seen by the interface to that data structure.
14:46:59
beach
jmercouris: For instance, if you represent an empty queue by a single CONS cell, then you remove the special case to test whether the head and the tail pointers are both NIL.
14:47:58
jmercouris
I'm thinking about what the last thing you said, why would we test to see if the head and tail is both NIL? to test for empty queue?
14:48:55
jmercouris
so we can just check if the cons cell is pointing to nil instead of performing two checks
14:49:12
pjb
makomo: (let ((e '(+ 1 2))) `(list ,e)) #| --> (list (+ 1 2)) |# you see that the comma evaluates E, but doesn't evaluate the result of E.
14:49:25
beach
jmercouris: You typically check whether the head and the tail point to the same thing in order to determine whether the queue is empty.
14:49:42
pjb
And if you double the quotes and comma, even less: (let ((e '(+ 1 2))) ``(list ,,e)) #| --> (list* 'list (list (+ 1 2))) |#
14:50:27
pjb
makomo: again, you need something to evaluate the result of the backquote. Such as a compiler calling a macro and generating the code to evaluate the result of the macro, or an explicit call to eval.
14:50:29
makomo
pjb: is the thing on the right supposed to represent a "raw list" or the equivalent form that when evaluated gives the same thing?
14:50:51
pjb
makomo: don't confuse who does what. It's eval, or compile that will set things to evaluate the subexpression (+ 1 2).
14:53:28
makomo
the thing that's not clear to me, is how to get to that behavior, using the formal rules of backquote described in the spec
14:53:56
makomo
so evaluating a doubly nested backquote form with ,,@a in it first splices in the elements of a, and then evaluates all of those elements
15:19:03
pjb
makomo: in reality both commas "expand" or substitute the result of expression following the comma. The only question is where the binding are sought. And this is where the parenthetical aspect of backquote and comma matters.
15:20:44
makomo
the question where the bindings are sought is a simpler problem to me and is easy to understand by analogy either with parentheses as you say, or with "ladders" (the so-called ladder algorithm or whatever it was)
15:20:54
pjb
(let ((e '(+ 1 2)) (v (gensym))) `(let ((,v ,e)) `(list ,',v ,,v ,',e ,,e))) #| --> (let ((#1=#:g7204 #2=(+ 1 2))) (list* 'list (list* '#1# (list* #1# (list* '#2# (list #2#)))))) #| --> (list #:g7204 3 (+ 1 2) 3) |# |#
15:21:13
makomo
the confusing thing for me is what does the left "," do with the expansion of ",@" in ",,@"
15:22:10
pjb
Notice in (let ((e '(+ 1 2)) (v (gensym))) `(let ((,v ,e)) `(list ,',v ,,v ,',e ,,e))) how we HAVE to use TWO commas to get the V and the E bindings that are in the outer LET.
15:22:45
pjb
On the other hand, a single comma is needed in the `(let ((,v ,e)), because there's only one ` to traverse.
15:25:52
makomo
pjb: here's an example of ,,@ in the wild, https://github.com/keithj/alexandria/blob/master/macros.lisp#L61
15:26:17
makomo
what once-only does is not important right now, but i do know what it does and how it does it
15:29:19
pjb
makomo: more interesting a question, is why the lambda body uses ``(,,g ,,(cdr n)) instead of (list g (cdr n))
15:31:15
pjb
makomo: AFAICS, there's no difference between ``(,,g ,,(cdr n)) and (list g (cdr n)). It's the programmer loving ` and ,…
15:34:33
makomo
the result of mapcar is a list of the form '(`(,a1 ,b1) `(,a2, b2) ...) where ai and bi are the results of evaluating g and (cdr n) in the i-th iteration of mapcar
15:35:50
makomo
the elements of this list are spliced in front of a commad, meaning that all of these elements will be evaluated in the second evaluation
15:37:06
pjb
makomo: to see what a reader macro reads something as, you can just type the form at the REPL, prefixed by quote, so that it's not evaluated, but just read.
15:38:00
pjb
in ccl, '``(,,g ,,(cdr n)) --> (list* 'list* (list* g (list (list* 'list (list (cdr n))))))
15:38:28
pjb
(setf *print-pretty* nil) ; but the printer for ` may do its work even without *print-pretty*.
15:39:25
pjb
yes, so ``,,g is needed, because g contains an expression that when evaluated will return a variable name…
15:40:40
pjb
Well, you can say that it evaluate the following expression in the lexical context outside the second ` on the left.
15:42:30
makomo
the inner backquote will be evaluated in the lexical context set up by the outer let
15:54:43
beach
Let's say I am using an ideal debugger, and I am stopped at the beginning of the form (f (g a) (h b)). Can we agree that if I execute a STEP OVER, command, then (g a) will be evaluated, then (h b), then the call to f will be made and I will find myself stopped at the end of the initial form?
15:56:41
beach
Now, if I am again stopped at the beginning of the form (f (g a) (h b)) and I execute the STEP IN command, can we then agree that I will probably find myself stopped at the beginning of the form (g a)?
15:58:42
beach
My question is: Suppose I want the next stopping point to be the beginning of the first form of the function F, where would the current stopping point have to be located, and what is the name of the command that I need to execute?
16:01:33
beach
One possible answer would be: From the initial situation, I would issue a STEP IN command, followed by two step-over, which would take me to the end of the form (h b). I would then execute another STEP IN. Since there is no form to step into after (h b), I will instead step into the function F.
16:03:50
beach
Another one would be: From the initial situation, I would issue a STEP IN command, followed by two step-over, which would take me to the end of the form (h b). I would then execute a command that has yet to be defined, say STEP CALL, in order to step into the function F, whereas executing a STEP IN in that situation would step into the form following (f (g a) (h b)).
16:40:01
pjb
beach: about no form after (h b) I would note that I would often like to see the result of the stepped over form before continuing (or possibly retry the stepped over form, stepping in this time). Therefore I would insert automatically a stopping point just before the call to the function F.
16:41:38
pjb
similarly when you're stepping inside a function, we want a stop point once the result is elaborated and we're ready to return, but before we unwind the scopes of the function, so that we may jump back inside the function to retry something.
16:42:10
pjb
Yes, after evaluating each argument. The thing is that stopping points can be fusionned if there are no evaluations between them.
16:42:59
pjb
We stop before and after arguments, before and after the call, before and after the return, etc. A lot of stopping will be merged.
16:43:30
pjb
Perhaps things would be more obvious looking at the binding scopes, when they're established and unwound.
17:21:31
puchacz
hi, I have a web application in Lisp (hunchentoot etc.). I would like to add a backend server in Java, that would not be accessible in the external world, but my Lisp web application would talk to it. What shall I use please? jfli, lisplets, foil, abcl maybe....?
17:22:02
makomo
sjl: when the defmacro is expanded, i want it to expand into something that will define a macro with the name being the current value of the symbol i gave it
17:24:38
sjl
(defmacro define-macro-named (symbol &body body) `(defmacro ,(symbol-value symbol) ,@body)) would work if you don't care about lexical bindings
17:24:47
makomo
sjl: i'm only playing around with a question that was asked earlier today. https://plaster.tymoon.eu/view/806#806
17:30:35
pjb
You can have another intermediate macro. But the thing is that the name of the ultimate macro will have to be known at compilation time anyways, so it has to be evaluated in the macro-expansion environment (or in the compilation environment).
17:31:08
pjb
makomo: the point is to know who provides the expression. In general, it's the macro defining macro that computes the names…
17:31:51
pjb
(defmacro define-a-bunch-of-macros (parameters) `(progn ,@(mapcar (lambda (stuff) `(defmacro ,(compute-macro-name stuff) …)) …))
17:35:00
pjb
If not a macro, I call the functions generate-something. Those generate functions can be called from macros.
17:50:05
makomo
using a macro is correct, but i have to evaluate the arguments within the expander, not the expansion
18:00:51
makomo
but now i want to create generate-maker that takes an expression that evaluates to the name of the class
18:01:30
makomo
how can i evaluate the expression bound to "name" without having to call EVAL or the like
18:03:28
Bike
for example you'd have (generate-maker (class-of (make-instance 'some-class))) expand into (defmacro whatever (args &body body) (make-instance (class-of (make-instance 'some-class)) :fun (lambda ,args ,@body)))
18:05:47
makomo
so if macro-function didn't exist, i.e. if there was no way to define a macro other than defmacro (i know it doesn't make much sense do provide such an api, but let's speak hypothetically)
18:06:01
makomo
then there would be no other way other than using EVAL within the body of the macro (not the expansion)?
18:07:28
TMA
makomo: well, as defmacro is a macro itself it expands to something that does its shenanigans. that something would be necessarily implementation defined though
18:08:02
makomo
true. so there will always be a non-macro way to do it, the only issue being whether it is an implementation detail of the api or not
18:09:36
makomo
so if for example CL didn't provide macro-function and the ability to set the macro function, you'd have no portable way of defining macros at run-time, right?
18:10:14
beach
Simpler question about debugger interface: suppose you have a sequence of forms to evaluate, like the body of a PROGN or the argument forms of a function call. Would you prefer to go from the beginning of one expression to the beginning of the next one, or from the end of one expression to the end of the next one?
18:10:15
beach
I am asking because as far as the program counter is concerned, the end of one expression is probably the same address as the beginning of the next. My question is more where you would like to see the cursor each time.
18:10:19
pjb
makomo: if you know that macro functions are functions taking two arguments: a form, and an environment, then yes.
18:11:03
pjb
beach: well, you need to stop at the beginning, but then it's better to stop at the end of each form to report the results of each form.
18:12:02
makomo
pjb: i still don't see how your code solves anything though. you're not calling define-maker anywhere
18:12:57
pjb
beach: in a gui, you can display things in different places, you can insert lines, etc.
18:13:12
pjb
beach: it's customary to have an indicator (and for breakpoints too) at the beginning of the line.
18:13:51
pjb
So I think the best would be to insert space between the two form to display the indicator or display things.
18:14:27
makomo
Bike: so is it true that to achieve what i want i would either have to use EVAL within the macro's body or expand into something like (setf (macro-function ...) ...) as you said?
18:14:43
pjb
Eg. could use a phylactery containing the results of the previous form, and pointing between the two forms.
18:15:03
pjb
It would work as well if the two forms are on the same line (eg. arguments) or on different lines (progn).
18:15:27
beach
pjb: But what if one is the condition of an IF and the other is far away (the ELSE branch)?
18:16:22
TMA
beach: that being said, the form about to be evaluated is ill-specified itself -- is it the innermost form to be evaluated? or the outermost? [say the "next" form is (cons (cdr x) (car x)) ... I don't know in what case I would expect the cursor be: _(cons (cdr x) (car x)) and in which cases (cons (cdr _x) (car x)) ]
18:16:31
pjb
Furthermore, the user could want to keep around some phylactery, "pinning" them so the arrows would have to be elastic to let the user move the phylactery around to read what's below.
18:17:15
pjb
beach: perhaps you could have a look at Racket. IIRC they have a debugger that does something like this.
18:19:17
beach
TMA: I was talking about the STEP OVER command, which does not enter into sub-forms. In your example, the next form would be the one following (cons (cdr x) (car x)).
18:20:18
pjb
On one hand, inserting stuff may be bothering. On the other, displaying windows above the source too. In any case, if it's automatic, it's better than if it requires manual manipulation…
18:21:10
TMA
beach: ok. in step over mode it is definitely the opening parenthesis ... but what is a step over mode? isn't the step-over step-into decision postponed until after some feedback is given to the user?
18:21:30
beach
pjb: I don't consider inserting stuff in the kind of window I am talking about. The user would have to specify "edit" at which point some editor would be executed with the file in question.
18:21:50
pjb
beach: some debuggers also have a "finish" command, to continue execution until the end of the current function.
18:23:10
pjb
step in would stop before arguments, step into would stop at the entry of the function.
18:23:37
TMA
beach: yes, but in the 'usual' debuggers the selection is only done after the cursor is shown, so you cannot decide where to put it based on the choice the user has not yet made
18:24:08
MichaelRaskin
Hm, yes, a diifference between going through every subform in the source of the given fnuction, and falling through into the calls is a useful difference
18:24:34
TMA
beach: on the other hand, the usual debuggers are concerned only with lines, they do not usually have more precise/granular locations
18:25:06
beach
TMA: Yes, that's why I am asking. If I wanted to be line oriented, I would emulate something like GDB.
18:25:44
TMA
and so there is usually no simple way to step into H but not into F or G given (f (g) (h))
18:26:09
pjb
that said, perhaps we don't need to do anything special for the arguments: they're forms like others, so when we're stepping we'll stop in front of them like the others.
18:26:25
MichaelRaskin
TMA: if you are issuing each step command manually, you can just choose differently when H is evaluated
18:26:47
pjb
If we add the stop point at the end of each form to report result, we get automatically the stop before calling the function after evaluating all the arguments.
18:26:57
beach
TMA: SI would go to the beginning of (g), SO to the beginning of (h), SC would step into H.
18:27:29
pjb
From the debugger point of view, we could consider that all argument lists are wrapped in a call to list, so step out would be usable during argument evaluation, to stop before the call.
18:29:11
pjb
beach: one thing to be cautious about: the function can be determined before, or after (or perhaps more pathologically anywere in between?) the evaluation of the arguments. So the debugger cannot assume the function until it has evaluated all the arguments, if it wants to keep the same idiosyncrasies as the implementation.
18:29:49
MichaelRaskin
I guess a thing from classic debuggers that could be useful is selecting a position and saying «do stepping until this becomes the execution position»
18:30:37
nirved
usually debuggers have two kind of "step", one for machine code, and another for the higher level mapping
18:34:39
pjb
(defun call (fname argforms env) (let* (fun (args (loop with fp = (random (1+ (length argforms))) for i from 0 for arg in argforms do (when (= i fp) (setf fun (fdefinitione fname env))) collect (evale arg env)))) (funcall fun args)))
20:17:17
makomo
beach: i've been thinking about your fcge (first-class global environments) idea and how you can implemement a sandbox with it
20:18:08
makomo
and i can't help but wonder why is it the case that so many dynamic languages that have EVAL usually promote the "EVAL is evil" thing
20:18:24
makomo
or rather, why don't they provide actual sandboxing functionality to make EVAL useful
20:19:29
makomo
(other than using it to EVAL stuff i control myself, rather than some arbitrary user input)
20:20:36
Bike
lisp family languages are the only ones i've seen that even treat environments as manipulable objects (and even then CL's are kind of shit)
20:21:35
makomo
even if you did implement your own DSL, you would still have to somehow dynamically compile and evaluate a user-written file
20:21:39
MichaelRaskin
Lua is minimalistic enough that whatever it can do with environment manipulation feels approximately as restricted as everything in Lua stdlib
20:22:15
makomo
you could walk the code and what not, but that's not really a good nor portable solution
20:23:41
makomo
MichaelRaskin: interesting. i thought lua might have something like this but i wasn't sure
20:23:52
MichaelRaskin
./theangrygm:post-1470.6fc7a7ec7a82cb9facfe6fb6721b3c4ecb535be5b50f29e5607a0302226c161a65445b0269b1c8e70dfd992ed93418d319ca2f4835d2c12e0828d44521cc7bd2.gaming-for-fun-part-1-eight-kinds-of-fun.html.txt:Between
20:26:52
Bike
For example if you call cl:write, it will refer to special variables, but it'll do that with whatever global environment it was compiled in
20:29:46
makomo
Bike: meaning that if CL:WRITE was compiled in env A and now i'm in B and CL:WRITE is available to me, calling it from B will try to access which env's variables?
20:30:40
makomo
and that would be bad because you couldn't for example rebind dynamic vars in B and have them affect what CL:WRITE would do?
20:31:32
Bike
if you rebind them there's no problem, but if you don't it would use whatever value is global in A
20:32:43
makomo
would it also use B's globals if i defvar'd them in B (i.e. not just make a temp dynamic binding, but a global one)?
20:36:08
makomo
i haven't yet carefully read beach's paper, so maybe i'm missing something, but i don't get why. so if i'm in B and i do (let ((*something* 10)) (something-compiled-in-A)), something-compiled-in-A will see 10. but if i'm in B and i do (defparameter *something* 10) (something-compiled-in-A) then something-compiled-in-A will see whatever *something* is bound to in A?
20:43:44
makomo
Bike: hm, i guess that actually makes sense. so aside from the fact that defvars, defparameters, setfs, etc. might not affect certain functions, is there anything else?
20:44:59
Bike
strictly speaking, any function that signals anything uses the special variable *break-on-signals*
20:46:44
makomo
so every environment would need a complete copy of all the functions, global variables, etc. to behave properly?
20:48:12
makomo
so is a sandbox then hopeless or just tricky (but not as tricky as code-walking and similar) to do?
20:49:59
Bike
You could define a standard library where all the functions take a global environment as an explicit argument and use it scrupulously, and then just give each environment wrapper functions that call those with the environment.
20:53:05
makomo
so not only would the implementation have to support FCGEs, but if you wanted it to be actually usable, it would have to reimplement the whole stdlib to also take these env arguments and then create wrappers that delegate to these
20:54:23
Bike
i guess alternately they could consult a global environment that's in a dynamic binding, which then has to be bound locally for any calls
20:55:33
Bike
Or you could force the sandboxed code to be in a nearly-CL that doesn't have variables like *break-on-signals* and can't use function designators and stuff.
20:55:40
MichaelRaskin
One of the problems with doing sandboxing without full implementation support and without implementing a transpiler from Common Lisp to Common Lisp is that you never know what expanding standard macros can leak
20:57:18
MichaelRaskin
Well, maybe it is reasonable to do implement a self-transpiler as a Common Lisp macro library.
20:59:31
MichaelRaskin
Basically, you would have a package where all the Common Lisp stuff is defined, but all the standard macros expand to something pre-vetted.
21:00:35
MichaelRaskin
For many things the HyperSpec gives semantically acceptable but inefficient expansions, maybe you should make sure that your macroexpand only gives expansions like this
21:02:16
MichaelRaskin
(compilation with optimisations could try making sure the expansions do not leak and using efficient ones whenever it is safe)
21:03:17
MichaelRaskin
Once everything is the wrappers you control, you make sure these wrappers also rebind all relevant global variables to use the desired FCGE contents
21:03:48
MichaelRaskin
And you make sure you use a reader that thinks this masquerade package is the real :common-lisp
21:04:34
makomo
oh, so just making sure that the macros' expansions don't leak then? when you said "self-transpilers as a Common Lisp macro library" i thought you meant something else (but i still don't know what you meant by that, i.e. what is self-transpiling here?)
21:07:18
MichaelRaskin
Well, you write in Common Lisp a set of functions and macros that allow you to compile Common Lisp (with your preferred environment treatment) to Common Lisp (with only standard-based assumptions, and probably some defacto standard extensions)
23:21:13
pillton
Bike: Would copy on write work to solve the sharing of objects across environments?
23:32:54
pillton
Is it not a good idea to maintain a link between the lexical variable and the current global environment in situations like (let ((x 1)) (defun example () (incf x)))?
0:19:24
ealfonso
how does a recursive macro work, is it dynamically expanded at runtime? I wrote a macro to retry an arbitrary form up to n times, retrying in case of a condition signalled. the problem is that n is not known at compile time
0:21:54
ealfonso
so currently I wrote a macro using loop, so it's not a recursive macro. the problem is that I'm storing the result into a variable, and forms that return multiple values don't seem to work correctly
0:22:19
Bike
(defun retrying (n thunk) (tagbody loop (handler-bind ((error (lambda (c) (declare (ignore c)) (go loop)))) (funcall thunk))))