* 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
* 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
[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 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-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
* 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
* 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
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).