freenode/#lisp - IRC Chatlog
Search
10:05:09
luis
White_Flame: no _need_ for a separate package.lisp file I don't think, just a common convention.
10:06:03
White_Flame
I tend to put exports where things are defined. In such a case, a reevaluated defpackage whines about the empty exports not matching (in sbcl)
12:53:52
jmercouris
and of course I could do `(markup:markup ,(user-interface::object-expression q))
12:54:17
jmercouris
however, I want to just be able to do something like (markup:markup (user-interface::object-expression q))
12:55:40
jmercouris
I want to take an object, and have it emit some SEXP by some function and have that be processed by cl-markup
13:18:40
jmercouris
I wwas thinking evaluate if it is one of my user-interface library forms, otherwise do not
13:20:10
jackdaniel
if I understand correctly, you will need to change the code anyway, so "lot's of code" argument goes away -- in that case what is the problem with using the function markup* in the library?
14:11:33
nij
In the source code, it seems that #'apply is ill-defined. https://bpa.st/NYNBC .. Indeed, the first cond drops you in an infinite loop apparently. And indeed, by copying the code and change each "apply" to "my-apply", I obtained a function #'my-apply that never ends. What's going on here?
14:13:38
edgar-rft
nij: (answer to your clschool question from a few hours ago) if you *need* AND/OR functions, for example as arguments to FUNCALL or APPLY, the the intended way is to use EVERY (function) instead of AND (macro), and SOME (function) instead of OR (macro)
14:14:27
Bike
this definition is for the case where the actual apply function is called, like because it's used as an argument to some higher order function.
14:15:55
phoe
https://github.com/sbcl/sbcl/blob/d4740e38bd4e541c29827d922737147b6813e8b3/src/code/list.lisp#L30
14:16:38
nij
edgar-rft: Oh! Indeed! #'SOME and #'EVERY are almost what I want, only did I need a curried version. I have (finally) implemented one myself.. but will switch back to currying those two standard functions. thanks :)
14:18:44
jackdaniel
it is ugly also from the programmer background - this definition is not technically correct and relies on information that is not available locally
14:19:21
Xach
nij: as kent pitman once said, the model you can use initially is: "built-in functions are given by god"
14:19:35
nij
Let's just talk about CAR as it's much shorter, and the problem seems to be the same :)
14:20:47
jackdaniel
beach: could it be implemented if we exploit the fact, that call-arguments-limit is an integer?
14:22:06
Bike
you can have (apply function arg1 ... argl) as (multiple-value-call function arg1 ... (values-list argl)), which is what sbcl does. and then since multiple-value-call is a special operator it's "fine" in that the magic is on the special operator
14:23:21
jackdaniel
n.b ecl does that to a certain number of arguments (64 if I recall correctly), after that it indeed passes arguments by other means
14:24:09
phoe
and also the namespaces for functions, macros, and specops are actually the same namespace.
14:24:58
phoe
beach noted in the previous discussion that it should have been written differently if this were clean code
14:25:12
Bike
here's a better example for you. write your my-apply, and then do (define-compiler-macro my-apply (function &rest arglist) `(multiple-value-call ,@(butlast arglist) (values-list ,(first (last arglist)))))
14:25:28
Bike
that's pretty close to how sbcl works, if you assume that the compiler macro expansion is always used.
14:25:32
beach
nij: Maybe you prefer the SICL definition of CAR: (defun car (x) (if (consp x) (cleavir-primop:car x) (if (null x) x (error...))))
14:27:41
beach
aeth: You would still have to connect the CAR function to whatever the hardware does.
14:29:50
phoe
the implementation can do whatever it wants while it implements the functionalities provided by symbols in the CL package
14:29:53
beach
nij: When you look at system code, in order to understand it fully, you need to understand how the system does compilation and bootstrapping.
14:29:59
jackdaniel
nij: you may treat it as an implementaiton detail - what Xach quoted is very accurate
14:30:56
nij
But I hope there's a way to tell if something magical is happening, without looking into the source of the implementation.
14:31:13
beach
nij: Again, when you look at system code, in order to understand it fully, you need to understand how the system does compilation and bootstrapping.
14:31:15
phoe
as I said X weeks ago, a Lisp implementation can implement the function CL:+ by sending a question to Mechanical Turk over the internet.
14:31:49
phoe
the list specifying where magics are would need to be maintained by each implementation, and I have no idea of what benefit would be other than for maintainers and implementers.
14:31:55
jackdaniel
nij: from the "outside" behavior operators specified as functions work like functions, period - nothing dirty happens
14:32:02
phoe
nij: yes, studying the implementation and asking people who are knowledgeable in how it works.
14:32:06
jackdaniel
you may spot something magical when you actually look into the implementation code
14:35:51
beach
I think you will find similar "magic" in every implementation. In SICL for instance, you will see (defclass standard-class (...) (...))
14:37:30
nij
But fine, as it's not practical too much, it depends on the implementors if they want to make it clear or not. And that's their personal choice.
14:37:37
phoe
if you see a CL symbol being defined somewhere, it means that you're already in the magic zone
14:38:46
beach
nij: Then, I would have to annotate pretty much every class definition of a system class. That can become quite annoying to read.
14:40:02
Bike
as jackdaniel noted, it's not magic that has any conceivable effect on a conforming lisp program.
14:40:27
Bike
that's why there's no provision for telling you about it. You only need to care if you're curious about how sbcl is written.
14:40:54
beach
nij: My defclass example is "magic" in that the metaclass of standard-class is standard-class so it has to exist in order to be defined this way.
14:41:31
Bike
things that are only special operators ARE magic in a sense that you can actually discern, which is why special-operator-p exists.
14:42:40
nij
beach: I don't understand class yet so I can't say.. but I believe it's doable. (but not practical)
14:44:32
beach
nij: That definition makes perfect sense in that it uses the meaning of well defined operators, so it's ideal that way. But it is not operational for the reason I mentioned. It is the purpose of bootstrapping to make it operational.
14:45:09
beach
nij: The code would have been much less understandable if I had used some specific notation for something that can be perfectly well notated with DEFCLASS.
14:45:22
jackdaniel
n.b that's one of many reasons why programmer is not allowed to redefine operators in cl package (that is - the package is locked)
14:48:24
jackdaniel
(defmacro defmacro (&rest args) (cerror "Are you sure?") (error "Macros are bad for you"))
14:52:45
_death
(handler-bind ((sb-ext:symbol-package-locked-error #'continue)) (defun () () ())) ... (()) => ()
14:54:40
nij
To see what 'car really contains, in particular, i want to see a piece of it as a specop.
14:54:51
jackdaniel
nij: the operator you are looking for is: (defun magical-p (symbol) (eq (find-package 'cl) (symbol-package symbol)))
14:55:41
jackdaniel
there are more magical packages surely, like sicl's primop package, but you know the drill
14:57:26
Bike
nij, i don't think you've really absorbed that there is NO CONFORMING WAY to see car as anything other than a function.
14:58:18
Bike
you cannot construct a conforming program in which the fact sbcl does compiler transformations sometimes is relevant. In a way that's the entire point of compiler transformations.
15:02:14
jackdaniel
I don't want to spoil the fun for you, but it may be even implemented as a C function :_)
15:02:17
phoe
in a way, that's where it stops being #lisp and starts being #sbcl or #ccl or #ecl or wherever else
15:05:18
Bike
if you want to get into sbcl guts, you'll need to find a corresponding source-transformation, ir1 transform, or ir2 transform. there are a few functions that approximate getting at them but nothing that's easy to use since it's all sbcl guts. if you just do M-. in slime it'll usually pop up that stuff.
15:06:35
Bike
for car in particular you want the source transform in srctran.lisp, and then if that fails it goes to the ir2 convert in generic/vm-macs.lisp which is hard to get details on because it's a "reffer" into a primitive structure.
15:07:49
Bike
this is in addition to other transforms and things that can sometimes elide calls to the car function that are spread throughout the system.
17:37:29
jackdaniel
hurrey, alpha-blended dashed path via xrender in McCLIM :) https://files.mastodon.social/media_attachments/files/105/634/554/978/109/689/original/cc3bbb1a7ed936b6.png
17:42:10
amerlyq
Hello, today I have two more questions on forms evaluations ;) 1) what will (defun f () (a) (b) (a)) will do if we replace
17:43:24
amerlyq
I expect (a) will keep previous value because stack frame stores pointer to whole previous environment of (f) ?
17:43:56
jackdaniel
amerlyq: I don't understand your question; I think that you could gain more insightful feedback if you put more effort in writing it
17:44:51
amerlyq
jackdaniel: It's not that I'm lazy, it simply is fuzzy in my head. Sorry. I will try to rephrase it.
17:45:34
OlCe
amerlyq: Hi. I don't think so. But also depends on whether you're compiling a whole file or not.
17:45:35
jackdaniel
my point is that it would be better if you first figure out what you are wondering about and then try to ask a precise question; that gives you a better chance of learning something
17:47:28
OlCe
amerlyq: then the second (A) could refer to the top-level definition of A, as well as the first.
17:49:01
OlCe
amerlyq: Then, if (A) is declared inlined, second call to A will be same as the first.
17:49:31
OlCe
And if you don't declare anything, and are not in the COMPILE-FILE case, then it's up to the implementation.
17:49:59
amerlyq
jackdaniel: so, I know, that (f) is executed in one thread, and we try to replace (a) from another thread. Current instruction pointer is at (b), so (a) was executed once already, and whole (f) is already on stack and captured pointer to environment, so further replacement of (a) must not have effect on current execution of rest of (f). But is it true?
17:50:15
jackdaniel
OlCe: thanks, that part about "regardless of whether (A) was declared inlined or not"
17:50:54
jackdaniel
amerlyq: common lisp standard does not talk about multithreading, so you are already in the unspecified territory
17:51:59
OlCe
jackdaniel: "A call within a file to a named function that is defined in the same file refers to that function, unless that function has been declared notinline. The consequences are unspecified if functions are redefined individually at run time or multiply defined in the same file."
17:52:35
jackdaniel
and whether environment capture, I don't think that there is such term in common lisp either - I doubt that environment is stack-allocated in case of global functions
17:52:55
OlCe
jackdaniel: So NOTINLINE blocks this behavior. And there is no real guarantee if there are runtime redefinitions.
17:53:28
jackdaniel
ah, that's what you've meant. I thought that you speak that inline/notinline declarations are ignored
17:53:56
amerlyq
OlCe: if "second (A) could refer to top-level" after compile time -- does it mean all symbols were resolved in compile time?
17:54:57
OlCe
jackdaniel: Although I would bet that, in practice, (F) calls the initial version of (A) in this case. But again, no guarantees about stable behavior.
17:56:07
OlCe
jackdaniel: Yes, I meant, whether there is an INLINE declaration or not (but not including the NOTINLINE case).
17:57:28
OlCe
Although it's not very clear, I interpret that an INLINE declaration on A would still give undefined behavior when using COMPILE-FILE.
17:58:08
amerlyq
Hm, ok. So we may expect CL still using old (A) until (f) ends. But it's not guaranteed :D
17:58:15
OlCe
In other words, I think that "The consequences are unspecified if functions are redefined individually at run time or multiply defined in the same file." also applies when A is declared INLINE. But that's debatable I guess.
17:58:49
OlCe
amerlyq: Yes, it seems. But, on the contrary, if you use NOTINLINE, then you know the new A will be used for the second call.
17:58:53
Bike
the compile-file restrictions are basically to allow the compiler to know things about functions in the same file so it can run analyses and possibly make calls simpler. it's left vague so that the compiler can choose not to do that.
18:01:27
OlCe
amerlyq: All-in-all, it would be much better to avoid this kind of manipulation altogether...
18:03:03
amerlyq
OlCe: but isn't it exactly what I get if I run event loop in some program AND edit it in emacs at the same time? So... otherwise I must deinitialize whole program to clear stacks, to avoid this problem.
18:10:20
amerlyq
In example above (defun f () (a) (b) (a)) I expect (f) refers to (a) by symbol name
18:10:58
Bike
if you want a redefinition of A to affect a loop in another thread, you can declare A notinline and it will probably work.
18:11:26
phoe
I assume it will work the same if you compile the whole file instead of going full REPL
18:12:16
amerlyq
phoe: sorry, some more context from above: we replace (a) from another thread, when (f) is currently running on stack (and evaluating (b) at the moment) directly before 2nd (a)
18:13:28
amerlyq
what you wrote in listing -- is replacing (a) from the same thread (i.e. environment), so of course (a) in this case will be redefined
18:14:20
amerlyq
but I seen somewhere explanation about each thread stack stores pointers to environment to allow executing previous versions of functions when stack is unwinded
18:15:27
phoe
but I do wonder if this behaves any other way on any other implementation under default optimization settings
18:18:24
amerlyq
But it means if I use resources (defun f () (unwind-protect (init-A) ... (deinit-A)) I still can't replace deinit-A until (f) is completed, or I will have bug
18:22:15
amerlyq
In my workflow I have both (init-A) and (deinit-A) together in file. I edit them symmetrically when needed and reload. But if event loop in another thread already called previous version of (init-A), then after ending event processing it will call new (deinit-A), which is wrong.
18:24:15
Bike
if i understand amerlyq's problem correctly, it's that deinit-a is being redefined AFTER init-a is called but BEFORE deinit-a is called, and the new deinit-a is inappropriate for undoing the consequences of init-a.
18:25:03
Bike
yes. i think this requires more synchronization than an implementation could provide without some input.
18:25:58
phoe
I know that Erlang has this sort of modularity in place, it supports live-reloading module by module, and a module contains multiple functions at the same time
18:26:56
amerlyq
I was able to found where I read mention about "multiple environment model" https://www.reddit.com/r/Common_Lisp/comments/9q6bum/how_does_common_lisp_implement_hot_code_reloading/
18:27:18
amerlyq
>> Each stack frame has its own environment frame, which allows you to "go back in time" and evaluate code in the context of a particular stack frame
18:27:57
phoe
eval-in-frame likely refers to dynamic context rather than an old version of the dynamic environment
18:28:06
phoe
eval-in-frame likely refers to dynamic context rather than an old version of the global environment
18:29:08
Bike
"It seems that in some way the LISP compiler updates also the call stack. " i don't think this is actually true.
18:30:22
phoe
but it requires the calling code to be prepared for such redefinitions, and still doesn't have any synchronization so is prone to race conditions
18:34:24
Bike
i don't understand how you could accomplish both allowing threads to see redefinitions from other threads, and making sure they see all redefinitions in a module at once, with preemptive multitasking?
18:37:34
amerlyq
if we combine init and deinit under single class, will instance use old or new deinit?
18:38:04
Bike
that is not how methods are organized in lisp. functions exist in themselves, they are not part of or under any class.
18:39:01
Bike
now, if you had like, (init object) and (deinit object), the choice of what method to call is delayed until the actual call, so if you redefined deinit it could work.
18:39:26
amerlyq
So, instances call methods too by name, and not by pointer (in which case instance would have still old ptr)
18:41:45
amerlyq
then, what is general workflow to mitigate such problems in your development? Do you really must reinit whole image when you with to replace some init-deinit pair?
18:42:39
Bike
I don't think I have ever run into this particular problem. If I did, I would probably stop the loop and redefine things, yes. If I really wanted to keep the process going I'd probably do something like what phoe wrote, maybe hidden by macros.
18:47:56
amerlyq
Bike: What about global state stored outside of loop? What if it still contains instances of old classes you changed? Will you try to transfer state somehow (assuming it was long to load/evaluate) or will ditch it too?
18:48:53
Bike
instances of classes that are redefined actually do get lazily updated to match the new definition.
18:50:23
OlCe
amerlyq: If you want to keep init-A and deinit-A in sync, a simpler way is to redefine the caller of them. It it's an event loop, then you should arrange for a way out of the loop after the caller's redefinition, and call again the caller.
18:51:03
OlCe
amerlyq: If not, you're going to have to hack the new version of deinit-A so that it runs the old code only if init-A has been called, which you may not be able to determine without a new version of init-A anyway.
18:51:26
amerlyq
Bike: Hm. Lazy update... (defclass Pt (x y)) -> (defclass Pt (a b)). After dropping x,y -- who will init values a,b , if their init was before the loop -- and therefore never called again?
18:52:29
amerlyq
OlCe: even if you redefine caller -- you still must redefine inidividual init/deinit -- and will still get inconsistency. Otherwise -- you must inline them for your approach to work
18:52:54
Bike
amerlyq: instances are updated at latest the next time you try to access one of their slots. so, if you then tried to access the A slot of a point, it would be updated, which means the value of the x slot would be dropped and the value of the A slot would be initialized from the initforms, and then the read of the a slot would complete and return that initialized value.
18:54:11
OlCe
amerlyq: Not necessarily, if your redefine the caller, you can substitute the calls to init-A' and deinit-A'.
18:56:19
amerlyq
Bike: and so, "A" slot will contain "default value" instead of expected values from "init" accompanying the "Pt" class. In demo of particles it will look like abrupt jump of all points to the (0,0) in the moment of reload.
18:57:08
OlCe
This way you just change the bindings to call the new versions of init-A and deinit-A in the new version only.
18:57:20
Bike
amerlyq: the instance updater doesn't know anything about your "init" function, no. You can however actually customize the updater to call your init method or whatever.
18:59:53
amerlyq
OlCe: thanks for the idea, batch-renaming functions by adding incremental sfx will allow somewhat atomic dirty replacement (still GC is crying).
19:00:29
amerlyq
dynvars and funcall looks like the way -- not "generic", but at least for some functions which I replace the most often.
19:02:00
amerlyq
Bike: I read above as "don't rely on lazy init and better manually reinitialize everything, however long it takes"
19:08:28
amerlyq
again example with particles simulation -- generally you expect to have some initial distribution -- so you have generator which creates batch of points with specific (x,y) based on its parameters. If you wrap this generator to generate single i-th point and provide it to lazy init -- you will "hardcode" generator parameters
19:09:37
Bike
Maybe I wasn't clear. The updater can look at the instance pre-update. You could for example assign A to have the old X value and B to have the old Y value.
19:09:52
Bike
But you were phrasing things in terms of an (init) call so I thought that was what you wanted.
19:13:20
Alfr
amerlyq, you could make your init-a return a corresponding deinit-a which the user of init-a shall call later.
19:13:34
amerlyq
Slightly convoluted example (I know): changing Pt(x,y) -> Pt(a,b) may signify you worked with geometric plane and wish to try working with phase plane (s,v) now (and still keep all previous program state). In this case there is no direct conversion (x,y) -> (a,b) and you must get new (a,b) values somewhere. Still let's stop, I know I must simply create PPt class instead :)
19:15:41
Bike
i kind of want to know how this is supposed to work. i mean if there's no correspondence between the old and new points, what does "updating" even mean?
19:16:51
Bike
I guess you could do something like call a function that returns both an initializer and deinitializer, then calls the initializer, then does whatever work, then calls the deinitializer. and then wrap it in a macro so you just write (with-a ... do work ...)
19:28:44
amerlyq
Bike: I had a mess of example, sorry. Emphasis was, that when you -add- new field -- it's often dependent on smth outside instance, so you can't lazy-init to generate proper value for that field
19:29:21
Bike
sure. in that case you'd probably have to do something more global, yeah. which i guess would involve pausing.
20:38:00
phoe
What sorts of fixes, corrections, revisions, additions, tricks, recipes, informations, would you like to see in the second edition of Common Lisp Recipes?
20:39:33
Xach
i preordered Common Lisp Recipes in 2015. between my preorder and the actual release, i moved to a new place. the book was delivered to my old house. the new owners kept it! i have never read it!
20:41:52
mfiano
A recipe for getting set up with a proper environment so we can direct people insisting on MS Notepad without SLIME/Sly over to it.
20:42:37
phoe
I will certainly want to have a whole initial section dedicated to emacs, vim, sublime, atom, and vscode along with their respective Lisp environments
20:44:10
_death
I couldn't bear to finish CLR.. every page had footnotes, which are not useful when reading front to back, and my compulsion forces me to read them.. too painful