Re: Aliasing methods in CPAN roles

2009-10-18 Thread David Green

On 2009-Oct-17, at 1:55 am, Jon Lang wrote:
This implies that both Logging and Math do Numeric, since the  
invocant ought to be of a type that the class does.


I would expect that
role Logging { method log(Numeric $x:) {...} }
means the invocant is really of type Numeric  Logging, without  
Logging having to do Numeric.  On the other hand, I can see that  
strictly that might not make sense, so perhaps I really do need to  
create a compound NumLog type first, so I can have method log(NumLog:)?


Or can I create a method outside of any role:
role Numeric {...}
role Logging {...}
method log(Numeric Logging $x:) {...}

(of course, that might be implicitly creating an anonymous compound  
type for me...)



I think that what you're actually looking for (for the purpose of  
illustration) is Logging::log:(Numeric $x:) and Numeric::log: 
(Numeric $x:).


Oh, yes!

If $x does Numeric and $x does Logging, then it has a class that has  
already encountered the potential conflict and resolved it in some  
way.  For example:


   class Baz does Numeric does Logging {
   method log(Numeric $x:) {$x.Numeric::log;}
   method log(Logging $x:) {$x.Logging::log;}
   } #`Baz postpones the decision until it knows which role it's  
being asked to play: Numeric or Logging.


Baz illustrates my proposal: if $x is a Baz, it will need to check  
the context to see if it's supposed to be acting like a Numeric or  
like a Logging, and will act accordingly - or it will complain about  
ambiguity if it can't figure out which role to play.  And the  
definition for Baz works because Logging does Numeric.


I suppose given that I want Logging's method log(Numeric Logging:)  
rather than its log(Any Logging:), the second method there should  
really be:

method log(Numeric Logging $x:) {$x.Logging::log;}

You cannot define a class that does Logging and does Numeric without  
defining at least one log method, because they conflict; and a class  
must somehow resolve all such conflicts.


OK; although it seems reasonable to have some sugar for the obvious  
kind of keep them all methods like in this example.  In fact, we  
probably have that already, by declaring a proto log that makes the  
others all work according to their sigs.  And my mistake was thinking  
that you could have the same sig doing different things, but really  
the second sig is log(Numeric Logging:).


(The only way to have the same sig twice would be something like if  
Logging defined a special version of log() for Numeric objects, while  
Numeric defined a special log() for Logging objects -- but  
semantically that ought to mean the same thing in both cases, so we do  
want a single method to handle that.)


In the Baz case, it addresses the matter by making two options  
available according to the role being played: Numeric or Logging.   
All you have to do then is to somehow indicate which role is being  
played.


If you can't tell by the routine's signature, my own preference  
would be to make it explicit by means of a given block:


   given Logging $x { .log } # logs like a Logging
   given Numeric $x { .log } # logs like a Numeric


I also thought given sounded good for this, but it would have to  
work differently from a normal given: if $x doesn't do Logging, then  
it needs to skip the block.  (Also, it looks very close to casting:  
given Logging($x).  Maybe something a bit more visually distinctive  
would be helpful, something like given $x as Logging, etc.?)



But I could see other alternatives:
   .log given Logging $x; # assumes the inclusion of a given  
statement modifier.


I think given, as either a modifier or a block, is the prettiest  
syntax.



   $x - Numeric $n { ... ; $n.log ; ... }


What I like about this is using a sig to apply the context, so no new  
syntax is needed.


(But I'll suggest something new for - in general: what if $x -  
Numeric with no $n variable were shorthand for $x - Numeric $x is  
rw, i.e. a shorthand that used the same variable name inside the  
block as the one being passed in?  That would be useful in cases like  
this where we don't particularly want to rename $x.)



   $x.log:(Logging:);



And I like this way because it's the most compact, inline way to  
indicate it.



-David



Re: Aliasing methods in CPAN roles

2009-10-18 Thread Jon Lang
David Green wrote:
 Jon Lang wrote:

 This implies that both Logging and Math do Numeric, since the invocant
 ought to be of a type that the class does.

 I would expect that
    role Logging { method log(Numeric $x:) {...} }
 means the invocant is really of type Numeric  Logging, without Logging
 having to do Numeric.  On the other hand, I can see that strictly that might
 not make sense, so perhaps I really do need to create a compound NumLog type
 first, so I can have method log(NumLog:)?

I think you should need to do this.

 Or can I create a method outside of any role:
    role Numeric {...}
    role Logging {...}
    method log(Numeric Logging $x:) {...}

 (of course, that might be implicitly creating an anonymous compound type for
 me...)

Last I checked, all methods must be members of a class or role.

 I think that what you're actually looking for (for the purpose of
 illustration) is Logging::log:(Numeric $x:) and Numeric::log:(Numeric $x:).

 Oh, yes!

 If $x does Numeric and $x does Logging, then it has a class that has
 already encountered the potential conflict and resolved it in some way.  For
 example:

   class Baz does Numeric does Logging {
       method log(Numeric $x:) {$x.Numeric::log;}
       method log(Logging $x:) {$x.Logging::log;}
   } #`Baz postpones the decision until it knows which role it's being
 asked to play: Numeric or Logging.

 Baz illustrates my proposal: if $x is a Baz, it will need to check the
 context to see if it's supposed to be acting like a Numeric or like a
 Logging, and will act accordingly - or it will complain about ambiguity if
 it can't figure out which role to play.  And the definition for Baz works
 because Logging does Numeric.

 I suppose given that I want Logging's method log(Numeric Logging:) rather
 than its log(Any Logging:), the second method there should really be:
    method log(Numeric Logging $x:) {$x.Logging::log;}

I suppose that that would work, too.

 (The only way to have the same sig twice would be something like if Logging
 defined a special version of log() for Numeric objects, while Numeric
 defined a special log() for Logging objects -- but semantically that ought
 to mean the same thing in both cases, so we do want a single method to
 handle that.)

And if you limit yourself to referencing types that the method's role
does, this won't be an issue.

 If you can't tell by the routine's signature, my own preference would be
 to make it explicit by means of a given block:

   given Logging $x { .log } # logs like a Logging
   given Numeric $x { .log } # logs like a Numeric

 I also thought given sounded good for this, but it would have to work
 differently from a normal given: if $x doesn't do Logging, then it needs
 to skip the block.  (Also, it looks very close to casting: given
 Logging($x).  Maybe something a bit more visually distinctive would be
 helpful, something like given $x as Logging, etc.?)

IMHO, given $x { ... } is effectively syntactic sugar for $x - $_
{ ... }, and given Numeric $x { ... } would be syntactic sugar for
$x - Numeric $_ { ... }.  If $x doesn't do Numeric, the default
behavior should be a fail.

   $x - Numeric $n { ... ; $n.log ; ... }

 What I like about this is using a sig to apply the context, so no new syntax
 is needed.

 (But I'll suggest something new for - in general: what if $x - Numeric
 with no $n variable were shorthand for $x - Numeric $x is rw, i.e. a
 shorthand that used the same variable name inside the block as the one being
 passed in?  That would be useful in cases like this where we don't
 particularly want to rename $x.)

It wouldn't always be workable; for instance, @a - Numeric, Stringy
{ ... } would grab the first two element of @a and would put them into
parameters; but there would be no obvious names to assign to those
parameters.

-- 
Jonathan Dataweaver Lang