A12: on inheriting wrappers

2004-04-30 Thread Aldo Calpini
let's suppose I want to build a class that keeps track of the objects it
creates. 

let's suppose that I want this class to be the base for a variety of
classes. 

let's suppose that I decide, rather than fiddling with the default
constructor, to wrap it up. 

something like:

class Animal {
our @.zoo;
new.wrap( {
my @results = call();
push(@.zoo, @results[0]);
return @results;
} );
}
class Lion is Animal { ... }
class Tiger is Animal { ... }
class Panther is Animal { ... }

my $simba = Lion.new();
my $shere_khan = Tiger.new();
my $bagheera = Panther.new();

my @all = @Animal::zoo; 
# @all should contain ($simba, $shere_khan, $bagheera);

will the above code work as expected, or is there something I've
overlooked?

cheers,
Aldo



Re: A12: on inheriting wrappers

2004-04-30 Thread Abhijit A. Mahabal
On Fri, 30 Apr 2004, Aldo Calpini wrote:

 let's suppose I want to build a class that keeps track of the objects it
 creates.

 let's suppose that I want this class to be the base for a variety of
 classes.

 let's suppose that I decide, rather than fiddling with the default
 constructor, to wrap it up.

 something like:

 class Animal {
 our @.zoo;
 new.wrap( {
 my @results = call();
 push(@.zoo, @results[0]);
 return @results;
 } );
 }
 class Lion is Animal { ... }
 class Tiger is Animal { ... }
 class Panther is Animal { ... }

 my $simba = Lion.new();
 my $shere_khan = Tiger.new();
 my $bagheera = Panther.new();

 my @all = @Animal::zoo;
 # @all should contain ($simba, $shere_khan, $bagheera);

 will the above code work as expected, or is there something I've
 overlooked?

For this particular case, I guess wrapping the BUILD submethod of Animal
would work as it will surely get called. Moreover, since it will be called
by BUILDALL in its own class the wrapper will work, regardless of how
wrappers get inherited.

But I suppose you were asking about wrapper inheriting in general...

 cheers,
 Aldo

--Abhijit


Re: A12: on inheriting wrappers

2004-04-30 Thread Abhijit A. Mahabal

On Fri, 30 Apr 2004, Aldo Calpini wrote:

 so I wanted to explore the possible interoperability of wrappers and
 classes. another example I can think of:

 role Logging {
 POST {
 foreach ( ::_.meta.getmethods() ) - $method {
 $method.wrap( {
 log($somewhere, calling $method);
 call;
 log($somewhere, called $method);
 } );
 }
 }

 class Foo does Logging { ... }

 does something like this make sense?

Sure, but where is inheritance involved here? Roles are composed into
classes, not inherited from. (That particular Role seems to be messing
with the class though.. its a trait rather than a role... and it will have
to be processed *after* all methods in the class are known (including
those that come from other roles)... all that may render what I say below
meaningless)

If further you said something like

class Bar is Foo {...}

and wanted that to do Logging for Bar methods, that is a different
question. But since you'd have only so many classes saying

class Bar is Foo does Logging {...}

isn't too much work, and this gives you the chance to control logging on a
class by class basis.

 cheers,
 Aldo

--Abhijit


Re: A12: on inheriting wrappers

2004-04-30 Thread Larry Wall
On Fri, Apr 30, 2004 at 11:14:55AM +0200, Aldo Calpini wrote:
: class Animal {
: our @.zoo;
: new.wrap( {
: my @results = call();
: push(@.zoo, @results[0]);
: return @results;
: } );
: }

That would almost certainly fail with an error saying that it couldn't
find your new subroutine.  The  sigil does not imply dispatch, and
the default .new is inherited, not autogenerated, last I checked.  :-)

Larry


Re: A12: on inheriting wrappers

2004-04-30 Thread Larry Wall
On Fri, Apr 30, 2004 at 06:00:27PM +0200, Aldo Calpini wrote:
: role Logging {
: POST {
: foreach ( ::_.meta.getmethods() ) - $method {
: $method.wrap( {
: log($somewhere, calling $method);
: call;
: log($somewhere, called $method);
: } );
: }
: }
: 
: class Foo does Logging { ... }
: 
: does something like this make sense?

Maybe, except that POST is for DBC, and is expecting you to return
a boolean value.  I have no idea who will call the POST block when,
if you put it on a class.  It might be called after every method
on every object, given that people will probably intend it to be
taken as an invariant that must always hold for the class.

So what you really want is LEAVE.  Except that probably fires off when
the role closure is left, rather than the class closure.  Remember that
things like POST and LEAVE are officially properties on the current
block, not methods.  And in fact, they aren't properties in themselves,
but merely add their closure to a queue that is stored in a property,
which probably ends up getting called internally with a .LEAVEALL or
some such, if there's any method name involved at all.

Larry


Re: A12 Versioning

2004-04-30 Thread Larry Wall
On Thu, Apr 29, 2004 at 01:36:39PM +0200, Aldo Calpini wrote:
: On Mon, 2004-04-26 at 16:20, Richard Proctor wrote:
:  Issues:
:  
:  1) Why does this only use Version and Author?  Suppose there are versions
:  for different oses or that use other particular libraries that are wanted
:  or not?
: 
: personally, I think this should be handled in the class itself.
: you always use DBI, don't use DBI-(Any)-(Any)-MSWin32 or ...-linux
: or ...-whatever. class name, version and author(ity) seem to me a good
: set to univocally identify a class.

Alternately, for OS-specific modules, it's part of the name as it is now, or 
possibly part of the author(ity).

:  2) Shouldn't all these properties just be in a hash against the module
:  rather than some having special significance?  Others can then be simply
:  added as appropriate.
: 
: see below.

Hashes are not as powerful as properties, and you can always treat the
properties as a hash, since you can treat any object as a hash.  But
we have to be sure to capture the information at compile time, not
run time.

:  3) Why is the information positional rather than by keyword property?
:use Dog :version«1.2.1» :author«JRANDOM» :os«cpm»;
: 
: probably because use itself can take parameters (or adverbs), and you
: don't want them to collide with the class metadata.

That is correct.

:  4) What are the expected rules for versioning?  While the public CPAN has
:  one set of versioning, other projects have weird versioning rules, with
:  letters and numbers.  What would work and why?
: 
: not to mention the fact that a lot of modules do things like:
: 
: $VERSION = sprintf(%d.%02d, q$Revision: 1.46 $ =~ /(\d+)\.(\d+)/);
: 
: which I think is a Good Thing, and should stay there.

That looks like one of those naughty run-time things that aren't
guaranteed to run soon enough.  I suspect we can make accomodations
for RCS/CVS without compromising that.  Maybe as simple as allowing

class Dog-$Revision: 1.46 $-JRANDOM

since it's a special parse anyway.  Wouldn't be the first accomodation
Perl made to other tools...  :-)

:  These are not fully thought through answers, but illustrate some thoughts
:  or the matter.
: 
: well, I have an idea on the matter. given that class definitions are
: closures to be executed by the class constructor code, there should be a
: context, somewhere, that knows about the current class being
: constructed. in this context (and no others, presumably), the .meta
: method could be given write-access to the metadata of the class.
: 
: so, you can say:
: 
: class Dog-1.21-JRANDOM { ... }
: 
: as well as:
: 
: class Dog {
: .meta.version = 1.21;
: .meta.author = JRANDOM;
: # and of course...
: .meta.description = This class implements camera obscura.;
: .meta.subject = optics boxes;
: .meta.language = ja_JP;
: # etc.
: }

Okay, but you'd have to be careful.  If the class refers to itself
before you define its full name, it must not try to dereference that
name prematurely.  Worse, you're back in the situation that you have
to compile the class before you can know its version, or you have to
rely on hacky conventions to extract the version, which stinks.

I suppose another way to hack around it is to set up the compiler
with an option to bail out and tell you the full name of the class
the moment it thinks it knows it.  That's also pretty stinky.

As I alluded to in A12, another way around it is to require that the
filename or filesystem or module database always identify the full
name.  We need that anyway for installed modules in order to allow
version coexistence.  That doesn't interact so well with revision
control systems that assume a single file for multiple versions,
however.  Unless, of course, we say that the file storing all the
revisions is what Perl is actually interested in.  Hmm...

Larry


Re: How do I do parametered roles?

2004-04-30 Thread Aaron Sherman
On Fri, 2004-04-30 at 17:58, Austin Hastings wrote:

   class Session
   {
 does ListOfMessages
  (
attribute_base = 'messages',
method_base= 'messages',
display_color  = 'black'
  );
 
 does ListOfMessages
  (
attribute_base = 'errors',
method_base= 'errors',
display_color  = 'red'
  );
 
 ...
   }

 Can I just do:
 
   role ListOfMessages($attribute_base, $method_base, $display_color) {...}

I think you want a combination of re-dispatch and delegation:

class Session {
role EventHandler {
# The one and only place you define the behavior
method clear() {...}
method add() {...}
method have() {...}
method get() {...}
}
class ErrorHanlder {
does EventHandler;
# Probably should do some funky re-dispatch thing
method clear_errors() { .clear() }
method add_errors() { .add() }
method have_errors() { .have() }
method get_errors() { .get() }
}
class MessageHanlder {
does EventHandler;
method clear_messages() { .clear() }
method add_messages() { .add() }
method have_messages() { .have() }
method get_messages() { .get() }
}
has ErrorHanlder $:errors handles clear_errors add_errors 
have_errors get_errors;
has MessageHandler $:messages handles clear_messages add_messages 
have_messages get_messages;
}

That should do what you want.

-- 
Aaron Sherman [EMAIL PROTECTED]
Senior Systems Engineer and Toolsmith
It's the sound of a satellite saying, 'get me down!' -Shriekback




RE: How do I do parametered-roles on member objects?

2004-04-30 Thread Austin Hastings
(Note more precise topic.)

 -Original Message-
 From: Aaron Sherman [mailto:[EMAIL PROTECTED]

 On Fri, 2004-04-30 at 17:58, Austin Hastings wrote:

class Session
{
  does ListOfMessages
   (
 attribute_base = 'messages',
 method_base= 'messages',
 display_color  = 'black'
   );
 
  does ListOfMessages
   (
 attribute_base = 'errors',
 method_base= 'errors',
 display_color  = 'red'
   );
 
  ...
}

  Can I just do:
 
role ListOfMessages($attribute_base, $method_base,
 $display_color) {...}

 I think you want a combination of re-dispatch and delegation:

 class Session {
   role EventHandler {
   # The one and only place you define the behavior
   method clear() {...}
   method add() {...}
   method have() {...}
   method get() {...}
   }
   class ErrorHanlder {
   does EventHandler;
   # Probably should do some funky re-dispatch thing
   method clear_errors() { .clear() }
   method add_errors() { .add() }
   method have_errors() { .have() }
   method get_errors() { .get() }
   }
   class MessageHanlder {
   does EventHandler;
   method clear_messages() { .clear() }
   method add_messages() { .add() }
   method have_messages() { .have() }
   method get_messages() { .get() }
   }
   has ErrorHanlder $:errors handles clear_errors
 add_errors have_errors get_errors;
   has MessageHandler $:messages handles
 clear_messages add_messages have_messages get_messages;
 }

 That should do what you want.

Well, not really:

1- It required a lot of text for little gain.
2- Most of the required text was redundant.

What I want is probably something like this:

  role ListOfMessages[$method_base]
  {
is Array;
handles (s/_$method_base/_messages/);

method clear_messages() { .clear(); }
method add_message($msg) { .push($msg); }

method have_messages() { return .#; }
# Isn't there a .# property for arrays?

method get_messages() { return $_; }
  }

which is close to the same size as the original implementation, but still
doesn't speak to collision of the two lists: messages and arrays. I can move
the data declaration into the class, but this loses encapsulation:

class Session
{
  has @:messages
does ListOfMessages«messages»;

  has @:errors
does ListOfMessages«errors»;
}

It would be even better, of course, if the role could introspect the
variable name (assuming there is one, which isn't always true) and default
to using that for its method_base.

=Austin



Re: How do I do parametered roles?

2004-04-30 Thread Luke Palmer
Austin Hastings writes:
 Suppose that I have, for example:
 
   class Session {
 has @:messages;
 
 method clear_messages() {...}
 method add_message($msg) {...}
 method have_messages() {...}
 method get_messages() returns Array {... font color=black ...}
   }
 
 And suppose I add to it:
 
   class Session {
 has @:messages;
 
 method clear_messages() {...}
 method add_message($msg) {...}
 method have_messages() {...}
 method get_messages() returns Array {... font color=black ...}
 
 has @:errors;
 
 method clear_errors() {...}
 method add_error($msg) {...}
 method have_errors() {...}
 method get_errors() returns Array {... font color=red ...}
   }
 
 So I realize that not only is the whole list-of-messages thing a packageable
 behavior (read: Role or Class) but that this particular behavior recurs
 within my class.

So what you want is a role whose names are private to the role, not the
aggreagating class.  Well, I believe private (i.e. $:foo) variables do
that anyway, so you can just abstract your messages out into a role and
it will work fine.  But if you read on, I'll offer some unsolicited
design advice.

 One solution is to compose member objects that implement this behavior:
 
   if ($my_object.messages.have_messages) {...}
 
 But that sucks.

Why?  From a design perspective, that's the correct way to do it.  What
if you have a routine that operates on message lists -- pruning them or
somesuch.  If you do it by renaming the methods, you break any kind of
future-proof code reuse that abstracting gets you in the first place.
But I'll agree that:

if $my_object.messages.have_messages {...}

Kinda sucks.  But you'll find that things that suck in this particular
way can be solved by tersifying your method names.

class MessageList {
method ready() {...}
method add($msg) {...}
method clear() {...}
method get() {...}
}

There's no reason to repeat _message on the end of each method when
you're already in a class about messages.  And then you have:

if $my_object.messages.ready {
say for $object.messages.get;
}

Which is quite more tolerable, IMO.

But this isn't an OO design list, it's a language design list.

If it turns out that role private variables leak, I might suggest making
a MessageList class as above, then making a role that knows how to turn
a MessageList into a set of methods.  Then you'd pass the role the
MessageList object which you instantiate yourself, and it would generate
the methods for you.  That's kind of a kludge, but you're using roles
for something they're not really intended for, so that's what you get.
Keep in mind that there are plenty of other ways to do what you want;
this is Perl, after all.

Luke