help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: EIEIO built in methods -- question


From: Eric Abrahamsen
Subject: Re: EIEIO built in methods -- question
Date: Thu, 13 Jun 2013 13:11:55 +0800
User-agent: Gnus/5.130008 (Ma Gnus v0.8) Emacs/24.3 (gnu/linux)

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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]