Hacker News new | past | comments | ask | show | jobs | submit login
Depending in Common Lisp (stevelosh.com)
112 points by Tomte on Aug 26, 2022 | hide | past | favorite | 6 comments



The novelty here is that the bulk of this is about the image nature of Common Lisp. The fundamental meta-class is all well and good and interesting, but this is about defining, and REdefining these structures in a living CL image and coping with the changes in place.

In contrast to most other systems, where the static build is simply that -- static. As a user, you don't have to worry so much about this. Not that it's not possible in other systems, it most certainly is. Other systems have very dynamic object structures. But they're typically bound within a "build and relaunch the world" metaphor of routine complete reset versus the "molding a live image" of the CL workflow.


Sorry, but I disagree. It's not about the "image nature" of CL (which, btw, is a very outdated way of using CL), but the interactive nature of CL. They're similar, in that a Lisp REPL has a memory image, but a running program in any language has a memory image.

Python, Ruby, Haskell, or any language with a REPL can experience this same problem, only in those languages there's not a good way to handle it.

Edit: To expand a little more, the "image nature" of CL has historically referred to dumping the in memory image to disk, quitting, and then loading that image back into memory later when the program starts again, and that's really an orthogonal concept to what TFA is talking about.


Not all programs in every language / implementations have a 'memory image' in RAM or virtual memory. There may also be different types of data. What they usually don't do is to dump and restart those memory regions, like a heap of code and data in something like many Lisp system. Notable exceptions are virtual machines for running operating systems or operating systems with sleep modes, where the whole running OS can be saved to external storage and be restarted.

Some Lisp system don't use images of the heap memory to save and restore programs - examples are some Lisp systems who use external C compilers to compile code to native code and Lisps who run on other software platforms which don't support image (like the JVM).

The other (mostly independent) dimension is that most Lisp systems/program still include development tools like: a read eval print loop, a debugger, a compiler, a code loader (a tool which can load source or compiled code into a running Lisp), possibly more IDE features, debug information, parts of the source code, ...

Many other language implementations have that (or a subset), too: Python, Ruby, ... though often the preferred development style is not to incrementally develop from a long running application which includes the development tools and/or which could talk to an external IDE.

The lifetime of a Lisp program and its IDE can be extended (to weeks/months/years) when it is saved and possibly restarted - thus the *need* to provide tools to integrate program changes increases and this leads to more ways how to update the running program. The Lisp and its program might not be built from scratch for a long time (weeks/months upto years).

If we had only a Lisp system like ABCL on the JVM (which can't save / load memory images, because of the missing support in many (all?) JVMs), there would be less need for incrementally developing with running development system, because there is no way to save the running system to disk -> most programs would often be built from scratch and the program would be started from loading code&data in some way. Features of incremental development might still be there, but not the bigger picture (sic!).

So I would still say that the way a program is developed and built has some influence how dynamic the program will be at runtime and vice versa.


> Portable programs must not define methods on SHARED-INITIALIZE.

TIL about this constraint and that it's only in effect when initializing classes. I think that I understood it once I thought about it for a moment.

It's possible that implementations have some of their internal code in primary methods for INITIALIZE-INSTANCE, REINITIALIZE-INSTANCE, UPDATE-INSTANCE-FOR-REDEFINED-CLASS, or UPDATE-INSTANCE-FOR-DIFFERENT-CLASS (The Four Horsemen of SHARED-INITIALIZE, if I may).

If the user supplies a method on SHARED-INITIALIZE, then it's possible for this method to run between the system-provided primary method on e.g. INITIALIZE-INSTANCE and the system-provided method on SHARED-INITIALIZE. If this happens, the user-provided method cannot depend on the object being in any meaningful state: the system-provided method on INITIALIZE-INSTANCE may leave the object in some sort of half-initialized state, with the system-provided method on SHARED-INITIALIZE being meant to finish the initialization. You don't want your code to go in between these two.

Hell, the MOP authors had a lot of foresight. (And experience, I guess.)


Hell, the MOP authors had a lot of foresight. (And experience, I guess.)

https://en.wikipedia.org/wiki/The_Art_of_the_Metaobject_Prot... is such a good read if you are seeking to stretch your mind :-)


Here's a famous post by the author, more for the "general public": https://stevelosh.com/blog/2018/08/a-road-to-common-lisp/

(that I complement with the last news: https://news.ycombinator.com/item?id=31646794)




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: