freenode/#lisp - IRC Chatlog
Search
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
1:09:02
Gnuxie[m]
Rust cannot be made lispy, mostly its investors are not interested in interactivity, but this isn't what you want to hear
1:10:56
Gnuxie[m]
That's Ignoring everything that makes rust rust stands against what makes lisp lisp
1:20:22
theemacsshibe
So, what kind of "built-in variable" would make Rust any more like Lisp exactly?