Common Lisp Object System (CLOS)
hescaide.me
hescaide.me
From what I can tell, it seems especially nice for a system that needs extreme flexibility. I like to program MUDs and rougelikes as side projects, and after initial exploration, CLOS seems perfect for that - it can enable certain interactions that I would otherwise have to spend a lot of work and design achieving in other languages. It turns what feels like a chore in other languages into something fun.
I would imagine that how much this is a pro or con depends upon the work you do. Some might see such potential flexibility as error prone and overly complex because the domains they work in don't require such complexity. But others might see it as the building blocks for a domain with complex interactions that they have to build regardless of the language they use.
For any application of substantial size, I can't imagine not using CLOS classes and generic dispatch. It may be overkill for small projects, but as the code complexity grows the abstractions of CLOS make their worth known. That's been my experience most recently with my 3D graphics system.
> OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them.
[http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...]
His reference to ‘LISP’ is, of course, referring to CLOS.
Definitely not. The all-caps "LISP" should be the strongest hint, but he name drops McCarthy and Carl Hewitt. McCarthy's LISP didn't have closures, so I'm thinking Alan is referring to something closer to Scheme. Which might make sense as Scheme is based on ideas from Hewitt (actors, message passing, etc.). But Alan isn't being terribly specific. You have to understand there are thousands of different "LISP" systems in the world. Many of which are just in some thesis or white paper.
> Depending on your definition, CLOS is or is not object-oriented. It doesn't support encapsulation.
Lack of encapsulation would preclude the protection and hiding of state-process.
One way to "privatize" slots or methods is to put them in a special package, which serves as a private namespace. This doesn't prevent programmers from seeing them but it can flag any use of them as "odd" style in the source code.
I don’t know the exact implementation details of CLOS, but I feel that it can be easily replicated in C by adding the needed metadata in the structure and doing the needed type checking in the functions.
And thus Objective-C was rebornYes I'm aware of the things you get in CL you don't get in Java: multiple inheritance, defmethod works on arbitrary types by default rather than having to create interfaces, better introspection.. but I don't care. If you're using these features so much there's probably something overly complex with your code to need to do so. Or you're working in such a complex domain you need to, in which case go ahead..
CLOS is an object-system construction kit as much as it is an object system. I generally like to solve problems by building a language in which the solution is simple and straightforward to express, and CLOS provides handy tools for defining data structures and suites of operations on them that facilitate that approach.
I also like to work by livecoding, and that sometimes means redefining types that have live instances. If my types are CLOS classes, the Lisp will automatically catch changed definitions and will update existing instances to conform to the new definition. That's awfully helpful in a livecoding context.
CLOS has also come in handy when I needed to build novel systems of data types and function dispatch. For example, Ive worked on several knowledge-representation systems over the years, and it's been useful to have CLOS features for constructing novel inheritance schemes, access controls, truth-maintenance, persistence mechanisms, and so on.
So I've used CLOS quite a bit over the years. Some tools vex me more every time I have to use them. Others are a greater pleasure to work with each time. In my experience, CLOS has been a tool of the second kind.
http://metamodular.com/SICL/call-site-optimization.pdf
(I think a modern version of Common Lisp would take this idea and run with it, making most or all built-in functions generic.)
CLOS really shines when it comes to problems that benefit from method combination, which allows one to decompose computations into reusable nuggets that can be reassembled using multiple inheritance.
I don’t know about that. To me, interfaces is the best thing since sliced bread, “OOP” or not. In particular, they bring some sanity to the design in the modern C++, where you would use templates in the lower level parts of the application, for performance, and interfaces at the higher level, for sanity.
As far as performance goes, it just hasn't been a problem for me, even when doing graphics and image processing. A pattern that's worked really well is to use OOP to structure and organize everything, and then have the implementation details flushed out with functional or imperative code. It's the best of all worlds.
And it's actually a good pattern in most OOP languages because it slows things down in most of them. The vtable in C++, extra GC in Java, object attributes vs. locals in Python, etc. Avoiding objects in performance critical code is just generally a good idea.
I agree with you, though, OOP should be something you reach for purposely, not as a default. And, unfortunately, the waters have been so muddied, that I couldn't tell you the best purposes to reach for it. About the best I can see is that it does lend itself well to metaphor, and people communicate almost exclusively in metaphors, when it comes to programming?
CLOS itself is great, however the decoupling of data and methods is something at times I find leads to harder to understand code.
I think that might be familiarity rather than anything fundamental. I often find the coupling of data and methods to lead to harder to understand code; if I want to define a new method on an object in Java, I have to either subclass it or modify the class itself. Even if the method is ancillary to the core functionality of the class. That seems strange to me.
> I want to define a new method on an object in Java, I have to either subclass it or modify the class itself.
maybe what you are looking for is extension methods?objective-c/swift/kotlin/c# all support them to varying degrees (but sadly not java afaik)
A quick read of the wikipedia article makes it look like in C# you can do this, but not in Ruby.
Sure, but recall that CLOS really consists of two distinct components. The class/object system, and generic functions.
Since CLOS is baked into the CL type system, generic functions will happily dispatch against DEFSTRUCT types (as well as all of the other types, not just objects). Add the ability to :include other DEFSTRUCTS, and the idea that when DEFSTRUCT B :includes DEFSTRUCT A, then the type-of of B is both as a B struct and an A struct, then you get back alley inheritance using structures.
So, now (in theory) you get the efficiency of DEFSTRUCT, but all of the dynamic dispatch giddy goodness of DEFMETHOD, when and if you want to use it.
It can be a really nice compromise.
That said, I mostly work on CL code by myself, maybe CLOS is more useful to large teams, maybe that's where it really shines and why people praise it.
If you meant one part of the system redefining things defined in another part, this can be addressed with package locking, if your CL implementation supports that (sbcl does). This is a general problem of lisps, not something specific to CLOS.
* 123
123
That looks good, let's see... * (describe *)
123
[fixnum]
Huh? What's a FIXNUM? * (describe 'fixnum)
COMMON-LISP:FIXNUM
[symbol]
FIXNUM names the built-in-class #<BUILT-IN-CLASS COMMON-LISP:FIXNUM>:
Class precedence-list: FIXNUM, INTEGER, RATIONAL, REAL, NUMBER, T
Direct superclasses: INTEGER
No subclasses.
Sealed.
No direct slots.
Uh-oh.Anyway, you see what I mean now by not being able to use Common Lisp without the CLOS. Unless by using the CLOS you mean having to explicitly opt-in by using classes, multiple dispatch, multiple inheritance, generic functions, the MOP and all that weird stuff.
Yes, that's what I meant. The fact that the CL type system maps into the class system doesn't mean we all code OOP. If you want to argue semantics, sure..
SBCL will generate _very_ different code for fixnums vs general objects, so much so that claiming that (+ 1 2) is using CLOS makes no sense.
HN's really starting to put me off with suchlike rife blanket-damning posts that are uninformative and usually come from people who have little experience.
This account is 52 days old, but the user behind this has been reading and posting on HN for a bit longer (let's say since PG decided to put HN online).
For the other comment, my point is that "genuinely wise" is much rarer nowadays, and instead we're drowning in "genuinely clueless". This is why you shouldn't learn about something's worth by reading HN comments nowadays. Instead you can put a limited amount of time actually studying the subject and then decide if it's worthy of more of your time or not.
I get further pissed off by having people argue back when I say something from my small island of expertise (SQL and a few other things) they clearly know less than me - I can't learn from them and they won't learn from me. I don't mind n00bs, we all were once, but to willingly remain n00bs by rejecting information, well I can't comprehend it.
I miss posts from the likes of BeeOnRope. The really good people get driven away.
You wouldn't go into why JavaScript is bad to point out that you don't need to write JavaScript to make a web page. You would just note that JavaScript was a late addition to the web and wasn't the first language usable on it.
Plenty of people were writing CL before CLOS existed.
You opined in addition on the quality of clos. That could have been very useful if you'd explained it.
But I agree about general data structures. Some of the sequence functions are generic but not enough of them. I presume this happened because circa 1990 generic dispatch was too slow to handle high speed data traversal. That's no longer true, and many libraries exist to "generify" more of Common Lisp.
This makes me think he's barely scratched the surface of what CLOS has to offer.
I met him at OOPSLA ‘89 (or it might have been at AAAI) and introduced him to a couple of Apple developer guys, because I thought oic was cool. Whether because of that introduction or other reasons I’m not aware of, John ended up selling oic to Apple for use as the basis of the ScriptX object system, which was the programming language of the Kaleida Media Player (https://en.wikipedia.org/wiki/Kaleida_Labs).
[1] https://mitpress.mit.edu/9780262610742/the-art-of-the-metaob...
[1] CLOS!? https://www.ca.crh.com/host-http-funcall.blogspot.be/2013/07...
It describes the thinking behind an implementation of OOP for Lisp, and along the process reveals parts of the mental journey to both Smalltalk and CLOS.
https://en.m.wikipedia.org/wiki/The_Art_of_the_Metaobject_Pr...
Defining a type: type MyType.
At any point one can declare method: F myMethod(..., mt:MyType, ...) some_code()
Anything defined by F is automatically a multimethod. When invoked, all methods in the multimethod are searched bottom up. The first one where the call arguments match the parameters (by type, with inheritance) is invoked.
That's roughly it.
Anyways Python's "class" is even worse:
class c: a=10
print c.a
c.a=13
print c.a
I cannot think any English word to replace the "class". The word I am thinking of could be translated as "box". "Box of things and improved versions of it".It is about classifying objects. You want to have a shared definition for objects that share a common set of properties. Those objects constitute a certain class of objects, defined by their commonality. To specify such a class of objects, you specify those shared properties. This is what classes do in programming languages, they provide a specification of a certain class of objects.
If you know that a given object belongs to a certain class, you then know that is has the properties specified by the class. For example, if you know that an object belongs to the class of String objects, then you know that it has a length property (or whatever).
“Class” is very similar to “type”, except that “type” is usually more general. But in languages where everything (every value) is a object, “class” and “type” can be virtually the same.
https://www.thesaurus.com/browse/class
naming is hard and class is a good name for it's meaning in software.
You haven't really got rid of classes though, you've just moved them from being explicit (something the language itself knows about) to implicit (something the developer keeps in their head). You still have some objects which play the role of "classes" (they are used as prototypes for other objects) and others which play the role of "instances" (no other object will use them as a prototype).
How much of an improvement is there really if the concept still exists, but the language itself is ignorant of it?
i = c()
print(i.a)
i.a = 42
print(i.a)Maybe instead of "Class" and "Object" we should use "Definition" and "Instance".
It just so happens that the object that describes a class's slots, inheritance, etc is ... an instance. Of what? A metaclass. Which is also an instance.
It's confusing at first but eventually it all gels.
Defflavor instead of defclass
“Many versions of Lisp added OOP through the addition of an object system called Flavors, which went beyond Smalltalk and included multiple inheritance and method combination.”
http://www.paulgraham.com/chameleon.html
“The power of flavors (and the name flavors) comes from the ability to mix several flavors and get a new flavor.”
https://franz.com/support/documentation/current/doc/flavors....
So it looks like the author discovered a key characteristic of OOP which many languages including Java, support.