4 ms·
Use Cases for Change-Class in Common Lisp
- kazinator 11y ago> Why would you change the class of an object rather than create a new object as a replacement? Because there might be references to the object all over the place, and updating all of those references to point to the new object might be a lot of work or even impossible. Logically speaking, updating all references to the new object would be equivalent to change-class. If you update all references to an object to a new one, you cannot tell that there is a different object. In fact there is nothing to tell; it isn't a different object. For instance if both X and Y in some expression (EQ X Y) previously point to the old object, then after this global update, both X and Y point to the new object. EQ returns T either way. (Think about copying garbage collectors.)
- jsnell 11y agoI think you might have misinterpreted that paragraph somehow? It's not saying that updating all the reference to point to the old object would have the wrong effect. It's saying that updating the references might not be feasible. A garbage collector has more access than an application programmer... There is an interesting exception to that in Smalltalk, where the become: operation will replace all references to one object with references to another.
- kazinator 11y agoIt says clearly "change the class rather than X". So X is not the same changing the class, right? Traversing the heap and actually replacing references could actually be a good trade-off. It means that class objects don't need to be implemented as handles which point to the real implementation, just in case someone uses change-class. It depends on how often change-class is done. change-class could tie in to a generational garbage collector. If change-class is done on a baby object, we know that the tenured objects do not point backward to that object, so we don't have to traverse them. Only other baby objects (and of course areas like stack frames and registers).
- jsnell 11y agoRight. The two things are different despite having the same ultimate effect, since (in the context of Common Lisp) one is a generic operation provided by the language, while the other is application-specific with no language support and would need a fragile bespoke implementation for every single use. If the discussion was about Smalltalk that provides become: but not CHANGE-CLASS, it'd be a different question.
- ScottBurson 11y agoCLOS class instances are implemented with handles for a very important reason: so slots can be added to classes dynamically, even if live instances exist. In decades of Common Lisp programming I think I've written a CHANGE-CLASS call maybe twice, but I add slots to classes all the time (and occasionally remove them).
- dragonwriter 11y ago> A garbage collector has more access than an application programmer... That's generally true, although there's no reason it has to be: a runtime could provide an API giving the application programmer equivalent access. It would probably constitute a giant foot-gun, though.
- joe_the_user 11y agoIn object-oriented languages (like c++ or java), changing an object's class usually involve "up-casting" or "down-casting". Such operations are pretty much by definition violations of some earlier architectural plan - either the plan of the program or the plan of some library you are using (the code in the article is modifying a thing passed to it by a third party library it seems). That doesn't say they are "wrong" since it may be more cost-effective to fix things "on the fly" but it seems like they are generally an indication you are entering the realm of "technical debt", stretching a plan beyond it's initial intentions.
- kazinator 11y agoCasting in C++ doesn't change an object's type. It changes the type of the reference or pointer through which the object is viewed. The run-time-type-info (RTTI) of the object is the same. Lisp change-class really changes the type. (There are no typed references in Lisp that correspond to what's going on in C++ with up and down casting; the object is just the object.)
- jlg23 11y agoThe author provides a good example of legitimate use of change-class with his compiler example. About violating "some earlier architectural plan" in the case of libraries: Being able to do this allows CL programmers to freely mix libraries with different "architectural plans" without resorting to some half-baked fusion architecture that has to account for the different styles used by the libraries. Last but not least, change-class is not a hack - it's behavior is well defined and documented. And when you consider that CL's "object orientation" is more generic that c++'s or java's, based on generic methods alone, inferring from c++- or java-paradigms to good CL style just does not work. Peter Seibel's talk "Practical Common Lisp" at google explains that pretty well: https://www.youtube.com/watch?v=VeAdryYZ7ak https://www.youtube.com/watch?v=VeAdryYZ7ak
- EdwardCoffin 11y agoIt's the kind of thing that is more useful in an image-based language. It can be very useful for debugging a running instance you've invested a lot of effort in. I've never used it in production code though. You could compare it to using a debugger to change the value of a variable in C or C++ in order to proceed with a program's run. For instance, you might notice that a your program should have updated a variable to correspond to some other data that was updated, so you make a mental note to add some code that will do that, manually change the variable yourself, and proceed. Edit: this post is probably a response to the recent post on the same topic: https://news.ycombinator.com/item?id=9928485 https://news.ycombinator.com/item?id=9928485
- ianbicking 11y agoI recall setting obj.__class__ in Python (kind of the same thing – though without the notion of slots), but I can't recall why that was useful. Hot-reloading is the one case I can think of (one of the things mentioned).
- nostrademons 11y agoIt's a good alternative to the State or Strategy patterns when you only need one per object and virtually all methods are affected. Using the pattern, you'd need to explicitly delegate all calls to the state/strategy object. When you change the class, you're effectively using the type object as the state/strategy object, which makes perfect sense because it already contains all the methods.