freenode/#lisp - IRC Chatlog
Search
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))))
0:37:08
Bike
establishing a bunch of blocks, including one named NIL, aorund arbitrary code is kind of bad though
0:38:46
makomo
Bike: because the blocks don't use gensym'd names, or because it's just complicated/bad style?
0:40:20
ealfonso
i'm trying this, https://pastebin.com/B68fQy5P => "return for unknown block: #:|loop-tag823|"
0:41:17
makomo
Bike: so what stndard looping construct is there that doesn't establish a NIL block? is there one even?
0:50:05
makomo
Bike: then i lose the ability to differentiate between exceeding and succeeding though, no?
0:50:19
makomo
i mean, currently i don't differentiate via the return value in any way, only in "Exceeding" being printed
1:00:29
ealfonso
thanks Bike makomo . I wanted to try it myself, and was only missing the (return-from ) and the named loop tag. https://pastebin.com/g3B3bidW
1:55:33
skidd0
Hello all. Quick question regarding packages and systems with asdf and quicklisp coming up.
1:56:19
skidd0
I have a package.lisp that exports two classes from my project.lisp along with a project.asd that defines the project's system
1:57:45
skidd0
but, when i try to then (make-instance 'class-in-project), i get an error that says the class symbol doesn't exist in common-lisp-user
2:08:30
Xach_
inheritance is done with use-package or :use in defpackage (it must be external in the source package)
2:09:05
Xach_
use-package isn't something you'd use normally - it's more typical to use defpackage to set things up.
2:12:35
Xach_
yes. but import has a bit of an issue where it doesn't matter if it's external. in theory you should import only external things. i'd like an import-if-external or something sometimes.
2:12:54
Colleen
Unknown command. Possible matches: 8, time, set, say, mop, get, login, grant, tell, roll,
2:16:32
simplegauss_
is there a standard way to create callable objects or (equivalently) add some annotation to a function object so that it can still be called by funcall?
2:18:51
simplegauss_
i've actually seen that, but i don't think that's standard, and moreover have no idea how it works (as in I can use it, but I have no understanding of the link between funcall and MOP).
2:18:58
simplegauss_
here is a working example: https://clos-mop.hexstreamsoft.com/concepts/#funcallable-instances
2:19:51
simplegauss_
is there a specification for how funcall actually works in the standard? my main reference is the hyperspec and as far as I can tell it makes no mention of anything to do with the mop
2:21:27
simplegauss_
Bike: i wanted something closer to the standard. i guess it doesn't really matter in practice, but it makes me uneasy that I don't have an understanding of how it works.
2:22:16
simplegauss_
is there a free way to learn about MOP? something like the hyperspec? it seems AMOP is only available for purchase
2:24:43
simplegauss_
Xach_: true :) really all i would need is a generic "funcall" that works on functions and on objects inheriting from a certain class, but that seems uglier and more invasive than using the existing MOP solution.
2:27:41
simplegauss_
Bike: what i'm confused about is, for example, I can't find in the spec you linked how funcall is modified, and this is the sort of thing i'd like to understand. do you know if that sort of info is in AMOP? if so I can try and get it from the library
2:28:38
Bike
it's just specified that if you try to call a funcallable-standard-object, it calls that object's funcallable-instance-function instead
2:30:48
simplegauss_
Bike: but the point is I can't make a "funcallable-simplegauss-object" that doesn't inherit from "funcallable-standard-object" but still works with funcall/apply, right? it just seems like a weird special case in the design.
2:32:10
Bike
you need the metaclass because the objects have an additional semi-slot for the function, which is reflected in the class
2:33:08
Bike
in the standard you can't define objects that aren't instances of standard-object, or classes that aren't subclasses of standard-class, just by design
2:36:10
simplegauss_
so historically CLOS came before and separately from the MOP infrastructure, or they were created concurrently but only certain parts made it into the standard?
2:45:31
simplegauss_
more generally, i have a (mostly academic) interest in the tradeoffs of polymorphism the CLOS/MOP vs typeclass (pioneered by Haskell) way. it seems that in practice the only deficiency of CLOS is return-type dispatch (which would be quite difficult in lisp :))
2:46:43
Bike
generic function calls will also involve a runtime dispatch cost that i believe haskell can avoid.
2:47:45
simplegauss_
I guess there's also the ML modules way, but that seems just a few macros away :)
2:48:47
simplegauss_
Bike: you might be interested in https://github.com/guicho271828/inlined-generic-function
2:49:52
Bike
the basic idea is that it implements CLOS using itself, and exposes some of the generic functions therefore involved.
2:58:16
ealfonso
let's say I want to split a single-package containing some utils into a utils and a 'core' file. defs in the 'core' file depend on defs made in util. do I need to put (defpackage ) in util?