* EIEIO built in methods -- question @ 2013-06-09 10:22 Eric Abrahamsen 2013-06-09 10:49 ` Eric Abrahamsen 2013-06-09 11:54 ` Pascal J. Bourguignon 0 siblings, 2 replies; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-09 10:22 UTC (permalink / raw) To: help-gnu-emacs There's not a lot of documentation out there about using EIEIO, so I want to check something. First of all I'm using emacs-version "24.3.1" (the archlinux package). This is so everyday in Python (the other language I have experience in) and so weird to me in (e)lisp that I'm experiencing a kind of cognitive clash, and want to make sure I'm not doing something very wrong. How does this look to anyone who knows EIEIO (or I guess CLOS)?: (defclass persistent-thing (eieio-persistent) () :documentation "Just for testing, :file slot comes from the superclass") (defmethod constructor :static ((thing persistent-thing) newname &rest slots) (let ((filename (plist-get slots :file))) (when (member filename my-big-bad-list-of-filenames) (error "There's already a thing writing to %s" filename)) (apply 'call-next-method thing newname slots))) Is this how we should be doing it? Essentially, Python's "**kwargs" turns into "&rest slots" --> (apply 'call-next-method .... slots). I'd be interested in writing up a small introduction about how to use "internal" methods, ie methods on the eieio-default-superclass. So far I've messed with constructor, destructor, object-print, and I guess initialize-instance (oooh, damn, I just realized I should probably be using initialize-instance rather than constructor in my above example). There are several more I haven't experimented with, but that should be documented. If I make a documentation patch against the manual, will someone with actual EIEIO/CLOS experience take a look at it? Thanks! Eric ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-09 10:22 EIEIO built in methods -- question Eric Abrahamsen @ 2013-06-09 10:49 ` Eric Abrahamsen 2013-06-09 11:54 ` Pascal J. Bourguignon 1 sibling, 0 replies; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-09 10:49 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen <eric@ericabrahamsen.net> writes: > There's not a lot of documentation out there about using EIEIO, so I > want to check something. First of all I'm using emacs-version "24.3.1" > (the archlinux package). > > This is so everyday in Python (the other language I have experience in) > and so weird to me in (e)lisp that I'm experiencing a kind of cognitive > clash, and want to make sure I'm not doing something very wrong. How does > this look to anyone who knows EIEIO (or I guess CLOS)?: > > (defclass persistent-thing (eieio-persistent) > () > :documentation "Just for testing, :file slot comes from the superclass") > > (defmethod constructor :static ((thing persistent-thing) newname &rest slots) > (let ((filename (plist-get slots :file))) > (when (member filename my-big-bad-list-of-filenames) > (error "There's already a thing writing to %s" filename)) > (apply 'call-next-method thing newname slots))) > > Is this how we should be doing it? Essentially, Python's "**kwargs" > turns into "&rest slots" --> (apply 'call-next-method .... slots). > > I'd be interested in writing up a small introduction about how to use > "internal" methods, ie methods on the eieio-default-superclass. So far > I've messed with constructor, destructor, object-print, and I guess > initialize-instance (oooh, damn, I just realized I should probably be > using initialize-instance rather than constructor in my above example). If you use initialize-instance rather than constructor, it looks like `slots' comes wrapped in an extra list layer, meaning you need (plist-get (car slots) :file). Hence the need for documentation! ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-09 10:22 EIEIO built in methods -- question Eric Abrahamsen 2013-06-09 10:49 ` Eric Abrahamsen @ 2013-06-09 11:54 ` Pascal J. Bourguignon 2013-06-09 15:22 ` Eric Abrahamsen [not found] ` <mailman.1279.1370791373.22516.help-gnu-emacs@gnu.org> 1 sibling, 2 replies; 17+ messages in thread From: Pascal J. Bourguignon @ 2013-06-09 11:54 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen <eric@ericabrahamsen.net> writes: > There's not a lot of documentation out there about using EIEIO, so I > want to check something. First of all I'm using emacs-version "24.3.1" > (the archlinux package). That's because it just tries to emulate CLOS. Use the Hyperspec or other Common Lisp tutorial or book covering OOP. http://www.lispworks.com/documentation/HyperSpec/Body/c_object.htm > This is so everyday in Python (the other language I have experience in) > and so weird to me in (e)lisp that I'm experiencing a kind of cognitive > clash, and want to make sure I'm not doing something very wrong. All right. But what is it you want to do? > How does > this look to anyone who knows EIEIO (or I guess CLOS)?: > > (defclass persistent-thing (eieio-persistent) > () > :documentation "Just for testing, :file slot comes from the superclass") > > (defmethod constructor :static ((thing persistent-thing) newname &rest slots) > (let ((filename (plist-get slots :file))) > (when (member filename my-big-bad-list-of-filenames) > (error "There's already a thing writing to %s" filename)) > (apply 'call-next-method thing newname slots))) > > Is this how we should be doing it? What "it"? > Essentially, Python's "**kwargs" > turns into "&rest slots" --> (apply 'call-next-method .... slots). Yes, lisp &rest has been translated to python **. > I'd be interested in writing up a small introduction about how to use > "internal" methods, ie methods on the eieio-default-superclass. The way to use "internal" methods is to NOT use them! That's why they're internal, because external code don't, must not, should not use them! > So far I've messed with constructor, destructor, object-print, and I > guess initialize-instance (oooh, damn, I just realized I should > probably be using initialize-instance rather than constructor in my > above example). There are several more I haven't experimented with, > but that should be documented. > > If I make a documentation patch against the manual, will someone with > actual EIEIO/CLOS experience take a look at it? So I still don't know what you want :-( -- __Pascal Bourguignon__ http://www.informatimago.com/ A bad day in () is better than a good day in {}. You can take the lisper out of the lisp job, but you can't take the lisp out of the lisper (; -- antifuchs ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-09 11:54 ` Pascal J. Bourguignon @ 2013-06-09 15:22 ` Eric Abrahamsen [not found] ` <mailman.1279.1370791373.22516.help-gnu-emacs@gnu.org> 1 sibling, 0 replies; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-09 15:22 UTC (permalink / raw) To: help-gnu-emacs "Pascal J. Bourguignon" <pjb@informatimago.com> writes: > Eric Abrahamsen <eric@ericabrahamsen.net> writes: > >> There's not a lot of documentation out there about using EIEIO, so I >> want to check something. First of all I'm using emacs-version "24.3.1" >> (the archlinux package). > > That's because it just tries to emulate CLOS. Use the Hyperspec or > other Common Lisp tutorial or book covering OOP. > > http://www.lispworks.com/documentation/HyperSpec/Body/c_object.htm > > >> This is so everyday in Python (the other language I have experience in) >> and so weird to me in (e)lisp that I'm experiencing a kind of cognitive >> clash, and want to make sure I'm not doing something very wrong. > > All right. But what is it you want to do? > > >> How does >> this look to anyone who knows EIEIO (or I guess CLOS)?: >> >> (defclass persistent-thing (eieio-persistent) >> () >> :documentation "Just for testing, :file slot comes from the superclass") >> >> (defmethod constructor :static ((thing persistent-thing) newname &rest slots) >> (let ((filename (plist-get slots :file))) >> (when (member filename my-big-bad-list-of-filenames) >> (error "There's already a thing writing to %s" filename)) >> (apply 'call-next-method thing newname slots))) >> >> Is this how we should be doing it? > > What "it"? > > >> Essentially, Python's "**kwargs" >> turns into "&rest slots" --> (apply 'call-next-method .... slots). > > Yes, lisp &rest has been translated to python **. > > >> I'd be interested in writing up a small introduction about how to use >> "internal" methods, ie methods on the eieio-default-superclass. > > The way to use "internal" methods is to NOT use them! > That's why they're internal, because external code don't, must not, > should not use them! > > So I still don't know what you want :-( Specifically: Override the initialization method for a class that inherits from eieio-persistent, to make sure that it's impossible to create two instances of the class that have the same :file slot. I've already realized constructor is the wrong method, so probably initialize-instance. Generally: If OO programming in (e)lisp is anything like OO programming in other languages, overloading initialization, deletion, and representation methods is *not* something to throw up our hands and wail about, it's a perfectly normal part of programming. See my specific example above. Unless I'm wrong about that, I'm offering to add a section to the manual detailing the right way to overload the basic methods on eieio-default-superclass, as a learning exercise for myself, if someone will agree to look at what I write, help me understand the issues correctly, and make sure what I write is accurate. That's all. ^ permalink raw reply [flat|nested] 17+ messages in thread
[parent not found: <mailman.1279.1370791373.22516.help-gnu-emacs@gnu.org>]
* Re: EIEIO built in methods -- question [not found] ` <mailman.1279.1370791373.22516.help-gnu-emacs@gnu.org> @ 2013-06-09 20:47 ` Pascal J. Bourguignon 2013-06-10 15:48 ` David Engster [not found] ` <mailman.1356.1370879310.22516.help-gnu-emacs@gnu.org> 0 siblings, 2 replies; 17+ messages in thread From: Pascal J. Bourguignon @ 2013-06-09 20:47 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen <eric@ericabrahamsen.net> writes: > Specifically: Override the initialization method for a class that > inherits from eieio-persistent, to make sure that it's impossible to > create two instances of the class that have the same :file slot. I've > already realized constructor is the wrong method, so probably > initialize-instance. Ok, initialize-instance won't do it, because make-instance doesn't return the result of initialize-instance, and it doesn't because the default constructor method doesn't. In CL, make-instance is a generic function so it would be a simple matter of overriding make-instance. In eieio, they've made make-instance a normal function and defered to the generic function constructor, so indeed I think you will have to define a method on your class for contructor. Now, the default method for constructor takes an instance of the class eieio-default-superclass. It would be easy if eieio was made correct, but it seems that have some deficiencies in the meta class domain: (class-of (make-instance 'c)) --> c ; in eieio (class-of (make-instance 'c)) --> #<standard-class c> ; in CLOS ; in eieio: (class-of (class-of (make-instance 'c))) Lisp error: (wrong-type-argument eieio-object-p c) ; in clos: (class-of (class-of (make-instance 'c))) #<standard-class standard-class> ; moral equivalent to eieio-default-superclass and foremost, it lacks EQL specializers! in CLOS, you could write: (defmethod constructor ((class (eql (find-class 'persistent-thing)))) …) but not so in eieio. For a start, eieio classes are not instances: (class-of (find-class 'c)) Lisp error: (wrong-type-argument eieio-object-p [defclass c nil nil [0 value 0] (value) (#2=#:unbound) (nil) [t] (nil) (nil) ((default)) (nil) (nil) ((:value . value)) nil nil #1=[] nil nil nil nil nil #1# [object c default-cache-object #2#] (:custom-groups (default))]) And finally, eieio defclass documentation says: Options in CLOS not supported in EIEIO: :metaclass - Class to use in place of `standard-class' so we cannot even define persistent-thing as having a different metaclass onto which we could define a specific constructor method. This indeed leaves us in a bad situation. You could either patch eieio, or wrap around it. I would advise you the later. If I started patching eieio, I would rewrite it entirely to make it a correct CLOS implementation… So instead of using make-instance to get your persistent-things, use your own function that will test for singletons: (defvar *file-things-map* (make-hash-table :test (function equal))) (defun make-persistent-thing (&rest arguments) (let ((file (getf :file slots))) (assert file) (or (gethash file *file-things-map*) (setf (gethash file *file-things-map*) (apply (function make-instance) 'persistent-thing arguments))))) > Generally: If OO programming in (e)lisp is anything like OO programming > in other languages, overloading initialization, deletion, and > representation methods is *not* something to throw up our hands and wail > about, it's a perfectly normal part of programming. See my specific > example above. Obviously, eieio failed to do that on the class level. (class-of instance) not being objects is a big FAIL. > Unless I'm wrong about that, I'm offering to add a section to the manual > detailing the right way to overload the basic methods on > eieio-default-superclass, as a learning exercise for myself, if someone > will agree to look at what I write, help me understand the issues > correctly, and make sure what I write is accurate. That's all. But let's not critisize eieio authors. AFAIK, they've just applied usual Agile directives, implementing an object system only to fulfill a very definite and limited goal, that of implementing cedet. Their product manager never had a budget to schedule a sprint about implementing CLOS in emacs lisp. The customer only wanted cedet, not CLOS (and if he wanted CLOS, he would know where to find it). Too bad your needs don't match exactly those of cedet. Time to scratch your itch yourself ;-) -- __Pascal Bourguignon__ http://www.informatimago.com/ A bad day in () is better than a good day in {}. You can take the lisper out of the lisp job, but you can't take the lisp out of the lisper (; -- antifuchs ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-09 20:47 ` Pascal J. Bourguignon @ 2013-06-10 15:48 ` David Engster 2013-06-11 2:16 ` Eric Abrahamsen [not found] ` <mailman.1356.1370879310.22516.help-gnu-emacs@gnu.org> 1 sibling, 1 reply; 17+ messages in thread From: David Engster @ 2013-06-10 15:48 UTC (permalink / raw) To: help-gnu-emacs Pascal J. Bourguignon writes: > Eric Abrahamsen <eric@ericabrahamsen.net> writes: > >> Specifically: Override the initialization method for a class that >> inherits from eieio-persistent, to make sure that it's impossible to >> create two instances of the class that have the same :file slot. I've >> already realized constructor is the wrong method, so probably >> initialize-instance. [...] > (class-of (make-instance 'c)) --> c ; in eieio > (class-of (make-instance 'c)) --> #<standard-class c> ; in CLOS [...] > and foremost, it lacks EQL specializers! These things should surely be documented in the manual (in the "CLOS compatibility" section). I'm not sure why `class-of' behaves this way; maybe it was to difficult to implement it the CLOS-way, or maybe Eric just did not know better. From reading the documentation, it seems he was thinking the class-of should return a name, since it says: -- Function: object-class obj Returns the class symbol from OBJ. -- Function: class-of obj CLOS symbol which does the same thing as `object-class' If you could provide a doc-fix for the EIEIO section regarding CLOS compatibility, that would be much appreciated, since you know much more about CLOS than I do. > So instead of using make-instance to get your persistent-things, use > your own function that will test for singletons: > > > (defvar *file-things-map* (make-hash-table :test (function equal))) > > (defun make-persistent-thing (&rest arguments) [...] Well, in some other OO languages, these singleton classes are usually implemented through static members. AFAIK, in CLOS you also have these in the form of slots with class allocation, which is also supported by EIEIO. In fact, if you look at the eieio-singleton class, this is also how it is implemented there. So, I guess you could implement a more specific singleton which check for the :file slot through something like this: (defclass persistent-thing (eieio-persistent) ((allfiles :type list :initform nil :allocation :class))) (defmethod constructor :static ((thing persistent-thing) name &rest slots) (let* ((filename (plist-get slots :file)) (file->class (oref thing allfiles)) (exist (cadr (assoc filename file->class))) newclass) (if exist exist (setq newclass (call-next-method)) (setq file->class (append file->class `((,filename ,newclass)))) (oset-default thing allfiles file->class) newclass))) Not as elegant as the "true CLOS-way", but it should work. > But let's not critisize eieio authors. AFAIK, they've just applied > usual Agile directives, implementing an object system only to fulfill a > very definite and limited goal, that of implementing cedet. Their > product manager never had a budget to schedule a sprint about > implementing CLOS in emacs lisp. The customer only wanted cedet, not > CLOS (and if he wanted CLOS, he would know where to find it). I guess you're speaking tongue in cheek here, still it should be mentioned that there was no "they". It's pretty much all Eric Ludlam's code, and I don't think agile directives were around in the mid 90's... :-) @Eric: If you'd like to improve the documentation, that would be much appreciated. I would definitely read over it, but I'm no expert in EIEIO, so it'd be best if you could send your contributions to emacs-devel or submit it as a bug report, so that others can also check it. -David ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-10 15:48 ` David Engster @ 2013-06-11 2:16 ` Eric Abrahamsen 2013-06-12 15:44 ` David Engster 0 siblings, 1 reply; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-11 2:16 UTC (permalink / raw) To: help-gnu-emacs David Engster <deng@randomsample.de> writes: > Pascal J. Bourguignon writes: >> Eric Abrahamsen <eric@ericabrahamsen.net> writes: >> >>> Specifically: Override the initialization method for a class that >>> inherits from eieio-persistent, to make sure that it's impossible to >>> create two instances of the class that have the same :file slot. I've >>> already realized constructor is the wrong method, so probably >>> initialize-instance. > > [...] > >> (class-of (make-instance 'c)) --> c ; in eieio >> (class-of (make-instance 'c)) --> #<standard-class c> ; in CLOS > > [...] > >> and foremost, it lacks EQL specializers! > > These things should surely be documented in the manual (in the "CLOS > compatibility" section). I'm not sure why `class-of' behaves this way; > maybe it was to difficult to implement it the CLOS-way, or maybe Eric > just did not know better. From reading the documentation, it seems he > was thinking the class-of should return a name, since it says: This discussion went over my head pretty fast! In my case I don't think I need to worry about the behavior of `class-of', I was just looking to tie some code into the creation of an object instance. Testing indicates that this works perfectly well: (defclass persisty (eieio-persistent) ()) (defmethod initialize-instance ((obj persisty) slots) (message "slots: %s" slots) (call-next-method obj slots)) I have access to `slots', can short-circuit initialization, and the object is created as expected. Surely that's enough? I like the idea of keeping the full list of files in a class-allocated slot. If it's a problem that the slot only exists after the first instance has been created, that seems like something that can be accounted for in `initialize-instance' as well... One thing I'm still confused about (and if I'm going to provide a doc patch, this is exactly what I'd like to address) is the relationship between `constructor', `initialize-instance' and `shared-initialize'. Obviously I can see that they call each other in that order, but I don't understand when you'd want to override one vs the other. As far as I can tell the only difference is that `constructor' has access to the string object name (the point of which I've never understood), and `initialize-instance' doesn't. Actually, thinking about this further, I should probably be using :after on my initialize-instance method above, to give the default superclass method a chance to assign any dynamically evaluated slot values. Or not? These are the questions I'm hoping to get answered. [...] > @Eric: If you'd like to improve the documentation, that would be much > appreciated. I would definitely read over it, but I'm no expert in > EIEIO, so it'd be best if you could send your contributions to > emacs-devel or submit it as a bug report, so that others can also check > it. Thanks for the offer! It would be something simple, just about using `initialize-instance', `constructor' if I ever understand exactly what that does, `object-print', and a few other methods in that neighborhood. As I mentioned I'm coming at this from Python experience, and wanted to write a basic how-to that would make sense to people with OO experience in other languages. Thanks Eric ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-11 2:16 ` Eric Abrahamsen @ 2013-06-12 15:44 ` David Engster 2013-06-13 5:11 ` Eric Abrahamsen 0 siblings, 1 reply; 17+ messages in thread From: David Engster @ 2013-06-12 15:44 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen writes: > This discussion went over my head pretty fast! In my case I don't think > I need to worry about the behavior of `class-of', I was just looking to > tie some code into the creation of an object instance. Since you want to meddle with the actual creation of objects, I'm afraid the issues Pascal mentions are at the core of the problems you're currently experiencing. I'll try to answer your question as best as I can, but my EIEIO&CLOS-fu is pretty rusty (and was never particularly good to start with), so please take the following with a grain of salt and prepend an 'AFAIK' in your mind. > Testing indicates > that this works perfectly well: > > (defclass persisty (eieio-persistent) > ()) > > (defmethod initialize-instance ((obj persisty) slots) > (message "slots: %s" slots) > (call-next-method obj slots)) > > I have access to `slots', can short-circuit initialization, and the > object is created as expected. Surely that's enough? But, as the name says, `initialize-instance' only initializes the newly created instance; it does not return it. So there's no way for you to return the existing singleton. The only thing you could do is throw an error. > I like the idea of keeping the full list of files in a class-allocated > slot. If it's a problem that the slot only exists after the first > instance has been created, that seems like something that can be > accounted for in `initialize-instance' as well... In initialize-instance, the instance is already created. > One thing I'm still confused about (and if I'm going to provide a doc > patch, this is exactly what I'd like to address) is the relationship > between `constructor', `initialize-instance' and `shared-initialize'. > Obviously I can see that they call each other in that order, but I don't > understand when you'd want to override one vs the other. In a nutshell: The 'constructor' actually creates a new instance and returns it, and it uses `initialize-instance' to initialize it, which again calls `shared-initialize'. This is confusing especially for people which come from a C++/Java background, because here these steps are inseparable and the details are fixed and defined by the language standard. For instance, in C++, you cannot change the order in which members are initialized from the initialization list. Also, what you call a 'constructor' in C++ is actually an initializer, because the object is already constructed when it is called. So the actual equivalent to what a constructor in C++ does is an 'initialize-instance :after' method. Since you want to meddle with actual object creation, you need to work on 'constructor'. As I've written, you can use class allocated slots to keep track of the already existing objects. However, as Pascal correctly mentions, you don't necessarily have an existing instance. This is why my initial statement that :allocate :class is pretty much equivalent to 'static' is wrong. However, there's `oset-default' and `oref-default', which also work on classes and not only on instances. All this is an EIEIO thing: the 'constructor' generic as well as `oset/oref-default'; this does not exist in CLOS (AFAIK, etc.). So how do you do this in CLOS? Here you have more powerful features: metaclasses (which Python has, too) and EQL-specialization. Both are not supported by EIEIO. > As far as I can tell the only difference is that `constructor' has > access to the string object name (the point of which I've never > understood), and `initialize-instance' doesn't. The object name is also an EIEIO thing. It's mainly there to ease debugging. For instance, we create lot of databases in Semantic (which are objects) to store symbols from source files in certain directories. The name of these databases is equal to the directory name. This makes inspection in the object browser much easier, since you directly see the directory instead of some cryptic code. However, you don't have to care about it, and `make-instance' will just use the class name by default. > Thanks for the offer! It would be something simple, just about using > `initialize-instance', `constructor' if I ever understand exactly what > that does, `object-print', and a few other methods in that neighborhood. > As I mentioned I'm coming at this from Python experience, and wanted to > write a basic how-to that would make sense to people with OO experience > in other languages. IMHO you best learn CLOS by forgetting OOP idioms from other languages. You'll just get confused. I recommend reading the two chapters on OOP from the Seibel-Book, which you can read online here: http://gigamonkeys.com/book/ -David ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-12 15:44 ` David Engster @ 2013-06-13 5:11 ` Eric Abrahamsen 2013-06-13 9:06 ` Eric Abrahamsen 2013-06-13 15:49 ` David Engster 0 siblings, 2 replies; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-13 5:11 UTC (permalink / raw) To: help-gnu-emacs David Engster <deng@randomsample.de> writes: > Since you want to meddle with the actual creation of objects, I'm afraid > the issues Pascal mentions are at the core of the problems you're > currently experiencing. > > I'll try to answer your question as best as I can, but my EIEIO&CLOS-fu > is pretty rusty (and was never particularly good to start with), so > please take the following with a grain of salt and prepend an 'AFAIK' in > your mind. > >> Testing indicates >> that this works perfectly well: >> >> (defclass persisty (eieio-persistent) >> ()) >> >> (defmethod initialize-instance ((obj persisty) slots) >> (message "slots: %s" slots) >> (call-next-method obj slots)) >> >> I have access to `slots', can short-circuit initialization, and the >> object is created as expected. Surely that's enough? > > But, as the name says, `initialize-instance' only initializes the newly > created instance; it does not return it. So there's no way for you to > return the existing singleton. The only thing you could do is throw an > error. Oh dear, something is very confused here! My understanding of the CLOS/EIEIO model of generic methods and inheritance is that you're basically just creating a glorified hook that gets called as one of a chain of hooks, and that you continue the chain with call-next-method. It's call-next-method that calls a method that produces a return value, not my actual implementation of initialize-instance. The whole thing in a nutshell: (defvar bad-file-name "/home/eric/bad.txt") (defclass persisty (eieio-persistent) ()) (defmethod initialize-instance ((obj persisty) slots) (let ((file-name (plist-get slots :file))) (if (equal file-name bad-file-name) (error "You can't use that file") (message "You made %s" file-name)) (call-next-method obj slots))) ELISP> (setq g (make-instance 'persisty "good" :file "/home/eric/good.txt")) [object persisty "good" "/home/eric/good.txt"] <--- produces success message ELISP> (setq b (make-instance 'persisty "bad" :file "/home/eric/bad.txt")) *** Eval error *** You can't use that file ELISP> b *** Eval error *** Symbol's value as variable is void: b Unless there's some potential fatal flaw with this that I'm not seeing, this is precisely the behavior I wanted. >> I like the idea of keeping the full list of files in a class-allocated >> slot. If it's a problem that the slot only exists after the first >> instance has been created, that seems like something that can be >> accounted for in `initialize-instance' as well... > > In initialize-instance, the instance is already created. > >> One thing I'm still confused about (and if I'm going to provide a doc >> patch, this is exactly what I'd like to address) is the relationship >> between `constructor', `initialize-instance' and `shared-initialize'. >> Obviously I can see that they call each other in that order, but I don't >> understand when you'd want to override one vs the other. > > In a nutshell: The 'constructor' actually creates a new instance and > returns it, and it uses `initialize-instance' to initialize it, which > again calls `shared-initialize'. Ha, though what you say here is basically a restatement of what I could see for myself in the code, it only just made sense, thank you. I think you're right -- I'm not used to seeing the initialization process broken into several steps, that was the only problem. I'm a little curious as to why overloading the constructor only seems to work with the :static specification (it doesn't appear to get called at all, otherwise). I'll do some more experimentation with class-allocated slots, that's still a bit opaque to me. [...] >> As far as I can tell the only difference is that `constructor' has >> access to the string object name (the point of which I've never >> understood), and `initialize-instance' doesn't. > > The object name is also an EIEIO thing. It's mainly there to ease > debugging. For instance, we create lot of databases in Semantic (which > are objects) to store symbols from source files in certain > directories. The name of these databases is equal to the directory > name. This makes inspection in the object browser much easier, since you > directly see the directory instead of some cryptic code. However, you > don't have to care about it, and `make-instance' will just use the class > name by default. That was interesting, thanks. For persistent objects, in particular, it does seem to make sense to use the save file as the name. >> Thanks for the offer! It would be something simple, just about using >> `initialize-instance', `constructor' if I ever understand exactly what >> that does, `object-print', and a few other methods in that neighborhood. >> As I mentioned I'm coming at this from Python experience, and wanted to >> write a basic how-to that would make sense to people with OO experience >> in other languages. > > IMHO you best learn CLOS by forgetting OOP idioms from other > languages. You'll just get confused. I recommend reading the two > chapters on OOP from the Seibel-Book, which you can read online here: I think I've got a handle on that now. I first read those chapters two or three years ago and was *totally* baffled -- I couldn't grok that methods could be separated from objects. I revisited it recently, though, and it made quite a bit of sense (and made me wish, once again, that my emacs ran on SBCL...). I don't think I'm necessarily stuck in Python/C++ etc, but I do think it's helpful to tell people "what it looks like over here". Thanks for your help, Eric ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-13 5:11 ` Eric Abrahamsen @ 2013-06-13 9:06 ` Eric Abrahamsen 2013-06-13 15:49 ` David Engster 1 sibling, 0 replies; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-13 9:06 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen <eric@ericabrahamsen.net> writes: [...] >>> Thanks for the offer! It would be something simple, just about using >>> `initialize-instance', `constructor' if I ever understand exactly what >>> that does, `object-print', and a few other methods in that neighborhood. >>> As I mentioned I'm coming at this from Python experience, and wanted to >>> write a basic how-to that would make sense to people with OO experience >>> in other languages. And, like an idiot, I just hadn't read the EIEIO manual thoroughly enough. The section on Default Superclass has a basic rundown of initialization, and the basic methods common to all classes. It doesn't mention `constructor', but it's certainly good enough. I also realized that `constructor' needs :static because it only works as a class-level method. The instance doesn't exist yet, so of course it needs to be called with a class as an argument. I certainly learned a lot from this discussion, though it doesn't look like I'll be able to give much back! E ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-13 5:11 ` Eric Abrahamsen 2013-06-13 9:06 ` Eric Abrahamsen @ 2013-06-13 15:49 ` David Engster 2013-06-14 1:37 ` Eric Abrahamsen 1 sibling, 1 reply; 17+ messages in thread From: David Engster @ 2013-06-13 15:49 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen writes: > Oh dear, something is very confused here! My understanding of the > CLOS/EIEIO model of generic methods and inheritance is that you're > basically just creating a glorified hook that gets called as one of a > chain of hooks, and that you continue the chain with call-next-method. > It's call-next-method that calls a method that produces a return value, > not my actual implementation of initialize-instance. `initialize-instance's return value is not used. It's just there to initialize the instance, which in EIEIO it delegates to `shared-initialize'. > ELISP> (setq g (make-instance 'persisty "good" :file "/home/eric/good.txt")) > [object persisty "good" "/home/eric/good.txt"] <--- produces success message > > ELISP> (setq b (make-instance 'persisty "bad" :file "/home/eric/bad.txt")) > *** Eval error *** You can't use that file > ELISP> b > *** Eval error *** Symbol's value as variable is void: b > > Unless there's some potential fatal flaw with this that I'm not seeing, > this is precisely the behavior I wanted. You can of course throw an error. However, this simply shifts all the hard work to the *user* of your class. It's him now who has to make sure that he is not creating a `persisty' for a file for which this was done already, or otherwise his application will throw an error. How he does that is up to him, but he has to do it every time he constructs an instance. Wouldn't it be much nicer if the class constructor would simply return the *already existing* instance instead? This is the main problem. You can achieve that through different methods, depending on what your language offers. The easiest way, which I guess should work in any OO-language, is to use a factory method or function. This is also what Pascal suggested with his `make-persistent-thingy'. However, this has several drawbacks in practice: you have to document it, and ideally you should forbid other ways to create objects of your type, which isn't always possible. The `constructor' however actually returns the instance of your `persisty', so he can return an already existing object. This is what my code did. You should replace the `oref' there with `oref-default', though. > I'll do some more experimentation with class-allocated slots, that's > still a bit opaque to me. This is explained very nicely in Seibel's book. > I don't think I'm necessarily stuck in Python/C++ etc, but I do think > it's helpful to tell people "what it looks like over here". IMO CLOS really begins to show its powers when you start to use multiple dispatch. Unfortunately, EIEIO does not support that... -David ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-13 15:49 ` David Engster @ 2013-06-14 1:37 ` Eric Abrahamsen 2013-06-14 15:14 ` David Engster 0 siblings, 1 reply; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-14 1:37 UTC (permalink / raw) To: help-gnu-emacs David Engster <deng@randomsample.de> writes: > Eric Abrahamsen writes: [...] >> Unless there's some potential fatal flaw with this that I'm not seeing, >> this is precisely the behavior I wanted. > > You can of course throw an error. However, this simply shifts all the > hard work to the *user* of your class. It's him now who has to make sure > that he is not creating a `persisty' for a file for which this was done > already, or otherwise his application will throw an error. How he does > that is up to him, but he has to do it every time he constructs an > instance. > > Wouldn't it be much nicer if the class constructor would simply return > the *already existing* instance instead? This is the main problem. Aha! Finally I understand what we're talking about. I hadn't even considered that as a potential solution to my issue -- I had no idea what you guys were talking about. In my case I'd almost rather warn the user that they're trying to do something bad, but I can certainly see where the silent short-circuiting would be handy. I see what your previous code is doing now. There's the eieio-singleton class provided as part of the package, which does the same thing in principle: only lets you create one instance of the class. The constructor: (defmethod constructor :STATIC ((class eieio-singleton) name &rest slots) "Constructor for singleton CLASS. NAME and SLOTS initialize the new object. This constructor guarantees that no matter how many you request, only one object ever exists." ;; NOTE TO SELF: In next version, make `slot-boundp' support classes ;; with class allocated slots or default values. (let ((old (oref-default class singleton))) (if (eq old eieio-unbound) (oset-default class singleton (call-next-method)) old))) This is the same idea, yes? I don't know why "name" and "slots" aren't passed to `call-next-method'. Anyhow, I appreciate all the ideas and pointers! Eric > You can achieve that through different methods, depending on what your > language offers. The easiest way, which I guess should work in any > OO-language, is to use a factory method or function. This is also what > Pascal suggested with his `make-persistent-thingy'. However, this has > several drawbacks in practice: you have to document it, and ideally you > should forbid other ways to create objects of your type, which isn't > always possible. > > The `constructor' however actually returns the instance of your > `persisty', so he can return an already existing object. This is what my > code did. You should replace the `oref' there with `oref-default', > though. > >> I'll do some more experimentation with class-allocated slots, that's >> still a bit opaque to me. > > This is explained very nicely in Seibel's book. > >> I don't think I'm necessarily stuck in Python/C++ etc, but I do think >> it's helpful to tell people "what it looks like over here". > > IMO CLOS really begins to show its powers when you start to use multiple > dispatch. Unfortunately, EIEIO does not support that... > > -David ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-14 1:37 ` Eric Abrahamsen @ 2013-06-14 15:14 ` David Engster 2013-06-14 16:15 ` Eric Abrahamsen [not found] ` <mailman.1680.1371226519.22516.help-gnu-emacs@gnu.org> 0 siblings, 2 replies; 17+ messages in thread From: David Engster @ 2013-06-14 15:14 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen writes: > There's the eieio-singleton class provided as part of the package, which > does the same thing in principle: only lets you create one instance of > the class. [...] > This is the same idea, yes? Yes. You can simply put eieio-singleton in your list of superclasses and you will inherit this behavior. > I don't know why "name" and "slots" aren't passed to > `call-next-method'. That's done by default. You only provide arguments to `call-next-method' if you'd like to override them. -David ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-14 15:14 ` David Engster @ 2013-06-14 16:15 ` Eric Abrahamsen [not found] ` <mailman.1680.1371226519.22516.help-gnu-emacs@gnu.org> 1 sibling, 0 replies; 17+ messages in thread From: Eric Abrahamsen @ 2013-06-14 16:15 UTC (permalink / raw) To: help-gnu-emacs David Engster <deng@randomsample.de> writes: > Eric Abrahamsen writes: >> There's the eieio-singleton class provided as part of the package, which >> does the same thing in principle: only lets you create one instance of >> the class. > > [...] > >> This is the same idea, yes? > > Yes. You can simply put eieio-singleton in your list of superclasses and > you will inherit this behavior. > >> I don't know why "name" and "slots" aren't passed to >> `call-next-method'. > > That's done by default. You only provide arguments to `call-next-method' > if you'd like to override them. Thanks! That's helps quite a bit. ^ permalink raw reply [flat|nested] 17+ messages in thread
[parent not found: <mailman.1680.1371226519.22516.help-gnu-emacs@gnu.org>]
* Re: EIEIO built in methods -- question [not found] ` <mailman.1680.1371226519.22516.help-gnu-emacs@gnu.org> @ 2013-06-14 18:29 ` Pascal J. Bourguignon 0 siblings, 0 replies; 17+ messages in thread From: Pascal J. Bourguignon @ 2013-06-14 18:29 UTC (permalink / raw) To: help-gnu-emacs Eric Abrahamsen <eric@ericabrahamsen.net> writes: > David Engster <deng@randomsample.de> writes: > >> Eric Abrahamsen writes: >>> There's the eieio-singleton class provided as part of the package, which >>> does the same thing in principle: only lets you create one instance of >>> the class. >> >> [...] >> >>> This is the same idea, yes? >> >> Yes. You can simply put eieio-singleton in your list of superclasses and >> you will inherit this behavior. >> >>> I don't know why "name" and "slots" aren't passed to >>> `call-next-method'. >> >> That's done by default. You only provide arguments to `call-next-method' >> if you'd like to override them. > > Thanks! That's helps quite a bit. Notice that in CLOS, you are forbidden to provide new arguments that would change the chain of methods that will be called: "When providing arguments to call-next-method, the following rule must be satisfied or an error of type error should be signaled: the ordered set of applicable methods for a changed set of arguments for call-next-method must be the same as the ordered set of applicable methods for the original arguments to the generic function. Optimizations of the error checking are possible, but they must not change the semantics of call-next-method." Since CLOS can dispatch on all the mandatory parameters, it's a strong constraint. Now, in the case of eieio, it can only dispatch on the first argument, so as long as you keep it, or an instance of the same class, you should be able to do as you wish. -- __Pascal Bourguignon__ http://www.informatimago.com/ A bad day in () is better than a good day in {}. You can take the lisper out of the lisp job, but you can't take the lisp out of the lisper (; -- antifuchs ^ permalink raw reply [flat|nested] 17+ messages in thread
[parent not found: <mailman.1356.1370879310.22516.help-gnu-emacs@gnu.org>]
* Re: EIEIO built in methods -- question [not found] ` <mailman.1356.1370879310.22516.help-gnu-emacs@gnu.org> @ 2013-06-10 19:18 ` Pascal J. Bourguignon 2013-06-11 15:43 ` David Engster 0 siblings, 1 reply; 17+ messages in thread From: Pascal J. Bourguignon @ 2013-06-10 19:18 UTC (permalink / raw) To: help-gnu-emacs David Engster <deng@randomsample.de> writes: > If you could provide a doc-fix for the EIEIO section regarding CLOS > compatibility, that would be much appreciated, since you know much more > about CLOS than I do. Unfortunately, I won't have the time to do so for the foreseable future (new job, etc). But if you try to do it yourself you'll learn a lot (read the Hyperspec, and/or about the MOP http://www.alu.org/mop/index.html (not that eieio tries to implement the MOP, but it'll give you some perspective, and some other references; http://www.dreamsongs.com/CLOS.html ). >> So instead of using make-instance to get your persistent-things, use >> your own function that will test for singletons: >> >> >> (defvar *file-things-map* (make-hash-table :test (function equal))) >> >> (defun make-persistent-thing (&rest arguments) > > [...] > > Well, in some other OO languages, these singleton classes are usually > implemented through static members. AFAIK, in CLOS you also have these > in the form of slots with class allocation, which is also supported by > EIEIO. In fact, if you look at the eieio-singleton class, this is also > how it is implemented there. Yes, for slots. But remember that methods are not attached to classes or objects in CLOS, but to generic functions. It's the generic functions who have methods, to implement themselves according to the classes of its argumentS. <-- Yes, it means multiple dispatch, in CLOS, but eieio lost all veleities of implementing multiple dispatch (an earlier version could do it, if badly, but not the later version). Anyways, if generic function dispatch on the class, then "static methods" are mere lisp functions. Hence (defun make-<your-class> ...) Also, remember that in lisp, things are orthogonal: there's no confusion of name spaces and classes. If you want a "static method" in a name space along with the other methods of the class, then you just put the symbols naming them in a separate package. Ah yes, emacs lisp doesn't implement (yet?) packages… Only obarrays, and since it has no reader macro, some C hacking will be required to implement packages in emacs lisp (or metalinguistically, as emacs-cl does it). Anyways, you can name them: (defun <my-class>-new (...) (make-instance '<my-class> ...) (defclass <my-class> () ((<my-slot> :accessor <my-class>-<my-slot>))) so that the prefix <my-class>- is your namespace. Remember, what :accessor does, is to define for you: (defgeneric <my-class>-<my-slot> (object)) (defmethod <my-class>-<my-slot> ((object <myclass>)) (slot-value object '<my-slot>)) (defmethod (setf <my-class>-<my-slot>) (new-value (object <myclass>)) (setf (slot-value object '<my-slot>) new-value)) ;; that's all. By the way, I don't know the code base of PCL, but now that emacs has lexical closures, it may be portable to emacs lisp without too much pain. > So, I guess you could implement a more specific singleton which check > for the :file slot through something like this: > > (defclass persistent-thing (eieio-persistent) > ((allfiles :type list :initform nil > :allocation :class))) Well you see, the problem of :allocation :class is that you need an instance to access it. When you're trying to construct an instance, you may not have one to get the :allocation :class slot. Or you can do the test after allocating the instance, but it's suboptimal. > Not as elegant as the "true CLOS-way", but it should work. If it does, it does. >> But let's not critisize eieio authors. AFAIK, they've just applied >> usual Agile directives, implementing an object system only to fulfill a >> very definite and limited goal, that of implementing cedet. Their >> product manager never had a budget to schedule a sprint about >> implementing CLOS in emacs lisp. The customer only wanted cedet, not >> CLOS (and if he wanted CLOS, he would know where to find it). > > I guess you're speaking tongue in cheek here, still it should be > mentioned that there was no "they". It's pretty much all Eric Ludlam's > code, and I don't think agile directives were around in the mid > 90's... :-) Yes, it's just that it reflects a mentality that is not often found in lisp culture. Lisp culture is rather to do the things right (cathedral) rather than to do them quick, and gain market share and popularity (bazar). Both have advantages. Personnally, I prefer to have fewer choice of libraries, but when I use one, to find that it is complete, instead of just implementing what was needed to implement some limited application. > @Eric: If you'd like to improve the documentation, that would be much > appreciated. I would definitely read over it, but I'm no expert in > EIEIO, so it'd be best if you could send your contributions to > emacs-devel or submit it as a bug report, so that others can also check > it. -- __Pascal Bourguignon__ http://www.informatimago.com/ A bad day in () is better than a good day in {}. You can take the lisper out of the lisp job, but you can't take the lisp out of the lisper (; -- antifuchs ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: EIEIO built in methods -- question 2013-06-10 19:18 ` Pascal J. Bourguignon @ 2013-06-11 15:43 ` David Engster 0 siblings, 0 replies; 17+ messages in thread From: David Engster @ 2013-06-11 15:43 UTC (permalink / raw) To: help-gnu-emacs "Pascal J. Bourguignon" <pjb@informatimago.com> writes: > David Engster <deng@randomsample.de> writes: > >> If you could provide a doc-fix for the EIEIO section regarding CLOS >> compatibility, that would be much appreciated, since you know much more >> about CLOS than I do. > > Unfortunately, I won't have the time to do so for the foreseable future > (new job, etc). But if you try to do it yourself you'll learn a lot There's a lot to learn out there. Unfortunately, time is sparse, and CLOS is not an itch I need to scratch at the moment. >> Well, in some other OO languages, these singleton classes are usually >> implemented through static members. AFAIK, in CLOS you also have these >> in the form of slots with class allocation, which is also supported by >> EIEIO. In fact, if you look at the eieio-singleton class, this is also >> how it is implemented there. > > Yes, for slots. > > But remember that methods are not attached to classes or objects in > CLOS, but to generic functions. Yes, I admit I keep forgetting that. Must be because of my C++ dayjob... [...] > Well you see, the problem of :allocation :class is that you need an > instance to access it. When you're trying to construct an instance, you > may not have one to get the :allocation :class slot. Or you can do the > test after allocating the instance, but it's suboptimal. It's the latter I was suggesting. >> I guess you're speaking tongue in cheek here, still it should be >> mentioned that there was no "they". It's pretty much all Eric Ludlam's >> code, and I don't think agile directives were around in the mid >> 90's... :-) > > Yes, it's just that it reflects a mentality that is not often found in > lisp culture. Lisp culture is rather to do the things right (cathedral) > rather than to do them quick, and gain market share and popularity > (bazar). > > Both have advantages. Personnally, I prefer to have fewer choice of > libraries, but when I use one, to find that it is complete, instead of > just implementing what was needed to implement some limited application. I think this is a wrong and unfair description of what EIEIO is and how it was developed, but I see no point in discussing this further. We try to list the deficiencies of EIEIO compared to CLOS so nobody has to waste time finding out it doesn't suit his needs, but obviously things are missing, and I will add your points when I have time to do so. If you have more issues that should be listed in the documentation, please let me know. -David ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2013-06-14 18:29 UTC | newest] Thread overview: 17+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-06-09 10:22 EIEIO built in methods -- question Eric Abrahamsen 2013-06-09 10:49 ` Eric Abrahamsen 2013-06-09 11:54 ` Pascal J. Bourguignon 2013-06-09 15:22 ` Eric Abrahamsen [not found] ` <mailman.1279.1370791373.22516.help-gnu-emacs@gnu.org> 2013-06-09 20:47 ` Pascal J. Bourguignon 2013-06-10 15:48 ` David Engster 2013-06-11 2:16 ` Eric Abrahamsen 2013-06-12 15:44 ` David Engster 2013-06-13 5:11 ` Eric Abrahamsen 2013-06-13 9:06 ` Eric Abrahamsen 2013-06-13 15:49 ` David Engster 2013-06-14 1:37 ` Eric Abrahamsen 2013-06-14 15:14 ` David Engster 2013-06-14 16:15 ` Eric Abrahamsen [not found] ` <mailman.1680.1371226519.22516.help-gnu-emacs@gnu.org> 2013-06-14 18:29 ` Pascal J. Bourguignon [not found] ` <mailman.1356.1370879310.22516.help-gnu-emacs@gnu.org> 2013-06-10 19:18 ` Pascal J. Bourguignon 2013-06-11 15:43 ` David Engster
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).