Re: unusual invocants
HaloO, On Sunday, 25. October 2009 01:38:21 Martin D Kealey wrote: Sounds like going back to static typing -- which does sometimes have some advantages. Well, you can also consider it dynamic. The important point is that it is a constraint on allowed types e.g. in the sig of a sub or on a variable. In my proposal they are handled through conversion on class level. One way to implement at would be to use proxy objects, which only do one of the roles (by passing them through to the appropriate methods on the original object). Do I understand you right that you propose to have a Dogwood class and two auxiliary proxy classes for the Dog and Wood roles? Isn't that too much effort? OTOH, a conversion routine could indeed return such a proxy if the original shall be kept unchanged. Regards, TSa. -- The unavoidable price of reliability is simplicity -- C.A.R. Hoare Simplicity does not precede complexity, but follows it. -- A.J. Perlis 1 + 2 + 3 + 4 + ... = -1/12 -- Srinivasa Ramanujan
Re: unusual invocants
On Wed, 21 Oct 2009, Ben Morrow wrote: The most important detail here is that the *class* gets to pick which imported methods need to be wrapped. [but] What this doesn't fix is that some other code (outside the class) will be expecting C::x to have T1::x semantics, and some will be expecting it to have T2::x semantics. If these are contradictory, there is no way to write an x which works. That's where the 'hats' idea comes in Sounds like going back to static typing -- which does sometimes have some advantages. One way to implement at would be to use proxy objects, which only do one of the roles (by passing them through to the appropriate methods on the original object). This could be done transparently to formal parameters, so that when they're used locally they would dispatch the expected method based on the locally declared type for the object. -Martin
Re: unusual invocants
Quoth tho...@sandlass.de (TSa (Thomas =?utf-8?q?Sandla=C3=9F?=)): Here is a direct syntax for the freeze feature of the paper: class C does T1 does T2 { freeze T1::x for foo; freeze T2::x for bar; method x {...} # for all other methods } The implementation is strait forward: on entry to foo and bar the dispatch table of the invocant is temporarily patched to contain the right x. After the call the original is restored. Isn't this just sugar for something like class C does T1 does T2 { method foo { my $tmp = self but role { method x { return self.T1::x } }; return $tmp.foo; } method x { ... } } (Excuse me if I have any syntactic details wrong.) The most important detail here is that the *class* gets to pick which imported methods need to be wrapped. Most of the time you want a method x in the class to be called from a method foo in the role: that's the point. What this doesn't fix is that some other code (outside the class) will be expecting C::x to have T1::x semantics, and some will be expecting it to have T2::x semantics. If these are contradictory, there is no way to write an x which works. That's where the 'hats' idea comes in, so you could write something like class C does T1 does T2 { method T1::x { ... } method T2::x { ... } } and have callers that thought they were getting a T1 call the appropriate override. However, there's still a problem with callers that know they have a C: which method do they get? AFAICS the only real solution to this is something like COM, where you say 'I would like to talk to this object as though it were a T1 now'. Might it be possible to use the type system to make this less painful than it usually is? Ben
Re: unusual invocants
HaloO, Ben Morrow wrote: Isn't this just sugar for something like Yes it is. My intent was to lighten the burden. I think we can agree that Ovid's problem can be solved by means of the current spec and some support syntax could be easily added. What this doesn't fix is that some other code (outside the class) will be expecting C::x to have T1::x semantics, and some will be expecting it to have T2::x semantics. If these are contradictory, there is no way to write an x which works. That's where the 'hats' idea comes in, so you could write something like class C does T1 does T2 { method T1::x { ... } method T2::x { ... } } and have callers that thought they were getting a T1 call the appropriate override. However, there's still a problem with callers that know they have a C: which method do they get? I have the following proposal to integrate the problem with the type system. We simply say that roles have an invocant slot in their type signature that is filled by the class they are composed into. That is 'class C does R' creates the type R[C:] which is distinct from R[A:] for 'class A does R'. These two are of course applicable where plain R is expected because this means R[Object:] and we have covariance in the invocant parameter. As a limiting case the role has type R[R:] that is an empty anonymous class with only the role composed. This can of course not be instanciated. Using that as a constraint e.g. in a signature or a container means that neither R[A:] nor R[C:] are applicable. However they can implement coercion routines C::R and A::R that convert the object and these are invoked when needed. So an extreme class might provide coercion routines to all its roles without loosing the identity of the object. And perhaps these might be generated automatically for easy cases. Consider the cartesian versus polar complex number example. Here one provides a role Complex and two classes Cartesian and Polar that do that role and implement conversion routines to each other. Then some optimized multi subs might be written that require Complex[Polar:] and Complex[Cartesian:]. Others just use Complex and are happy with both representations. AFAICS the only real solution to this is something like COM, where you say 'I would like to talk to this object as though it were a T1 now'. Might it be possible to use the type system to make this less painful than it usually is? The invocant slot of the role signature is sort of implied in the spec already! I also like this because a type in Perl 6 is then always written as SomeRole[SomeClass:]. Classes without explicit roles are Any[SomeClass:] and untyped is Any[Object:]. Note that a class C doing multiple roles spawns several types R1[C:], R2[C:], etc and the class name is a short form of their juxtaposition. So a typical CPAN module might provide a role together with classes that implement it in different ways and the users can nicely choose between them and add their own implementations. Users and such split modules can then use the roles and classes as bases for their own development. Note that this also allows more specific signatures like foo( Order[::T:] $x, Order[T:] $y ) which guarantees comparability of $x and $y without relying on mixed case MMD. Also we might have explicit specializations Order[Real:] that provides , =, and = in addition to before and after. Likewise for Order[Str:] which is of course not applicable to Order[Real:] without coercion of Str to Num which does Real. The default Rat is then just Rat[rat64:] for example. And the instanciating class might also be defaulted lexically. Essentially all types are then denominated in this form which uniquely identifies a role/class combination by means of the colon in brackets after the name. Regards TSa. -- The unavoidable price of reliability is simplicity -- C.A.R. Hoare Simplicity does not precede complexity, but follows it. -- A.J. Perlis 1 + 2 + 3 + 4 + ... = -1/12 -- Srinivasa Ramanujan
Re: unusual invocants
- Original Message From: TSa (Thomas Sandlaß) tho...@sandlass.de So what the OP wants to do is declare a method that is available on all those invocants - and only those invocatnts - which do all of roles X, Y, and Z. Granted, you can declare a new role XandYandZ that does X, Y, and Z, and define the method there, but that won't work on $foo unless you declare explicitly '$foo does XandYandZ' . The goal is to have the method show up no matter how $foo comes to do all three roles. Right. I have difficulty seeing the need for a method here. The distinguishing feature of a method is the access to the private data of an object that can hardly be granted by doing the three roles X, Y and Z. After all there's no unique implementation of these roles! I believe I started this thread, unless we mean John Lang as the OP after the rename. Private state is actually a separate issue and one which the traits researchers already dig into. I was asking the special case where: 1. A class consumes two (or more) roles 2. Each roles provides a method with an identical signature 3. The methods are not equivalent and neither role can rely on the other's method With that, you have roles which cannot be composed. You must rewrite one (bad if you don't own it), or omit one.. Cheers, Ovid -- Buy the book - http://www.oreilly.com/catalog/perlhks/ Tech blog- http://use.perl.org/~Ovid/journal/ Twitter - http://twitter.com/OvidPerl Official Perl 6 Wiki - http://www.perlfoundation.org/perl6
Re: unusual invocants
On Wed, Oct 21, 2009 at 3:47 AM, Ovid publiustemp-perl6langua...@yahoo.com wrote: I was asking the special case where: 1. A class consumes two (or more) roles 2. Each roles provides a method with an identical signature 3. The methods are not equivalent and neither role can rely on the other's method With that, you have roles which cannot be composed. You must rewrite one (bad if you don't own it), or omit one.. Rather than disallow the composition, I'd say that any class, role, or object that does both roles must override the method in question. Which takes us back to Jon's branch of the thread: it would be nice to be able to declare such an override in a general way that will apply to any such composition that doesn't otherwise override it locally. But what should that declaration look like? -- Mark J. Reed markjr...@gmail.com
Re: unusual invocants
Jonathan Worthington wrote: Ovid wrote: I was asking the special case where: 1. A class consumes two (or more) roles 2. Each roles provides a method with an identical signature 3. The methods are not equivalent and neither role can rely on the other's method With that, you have roles which cannot be composed. You must rewrite one (bad if you don't own it), or omit one.. When a role is composed into a class, it does quite literally become as if it were declared within the class itself (appears directly in the methods list), but equally does not lose its lexical scoping relationship with the role it was declared in either. Would it help to say that when a method declared within a role invokes another method, then we first search the methods within that role's lexical scope? Therefore: role Drinking { method buy_beer() { self.go_to_bar(); ... } method go_to_bar() { ... } } role Gymnastics { method go_to_bar() { } } class DrunkGymnast does Drinking does Gymnastics { method go_to_bar() { # something to resolve the conflict here } } This way, the method buy_beer will always consider methods in its lexical scope first and thus find the role's go_to_bar rather than the one in the class. Of course, if the role's lexical scope had no methods of that name declared within it we'd go elsewhere. This is close to what I've been suggesting in terms of checking which hat the object is wearing (or, alternately, which role it is performing). The main difference is that the final say _must_ be given to the class, because only the class knows enough about the implementation to be sure to do the right thing. For instance, what if you want the DrunkGymnast who goes to the bar in the Drinking sense to automatically be given a citation for doing so? class DrunkGymnast is the place where this issue must be addressed. Or worse: if you have her go_to_bar in the Drinking sense, you set a flag that indicates that she's drunk; and if you have her go_to_bar in the Gymnastics sense while she's drunk, the call should fail. This can only be done if you can define two distinct go_to_bar methods within the class, because Perl no longer has a want mechanism that would allow one method to handle both cases. This is where my proposal for disambiguating the two of them according to the invocant comes in. Ovid need not be right about his statement #2: while the two methods have the same short names (e.g., go_to_bar) and accept the same arguments (e.g., none), they don't necessarily have the same signatures, because they can use the invocant's type to address the in the Drinking sense and in the Gymnastics sense concept that I was using in the previous paragraph. As such, the two methods can have the same names and the same parameter lists, but still have different signatures (and thus different long-names): go_to_bar:(Drinking:) and go_to_bar:(Gymnastics:). The trick would lie in making the compiler smart enough to DWIM in most cases (by figuring out for itself which sense you mean), and in providing an easy-to-use means of explicitly choosing a sense to cover the remaining cases. I have some more thoughts on this; but I'm on a time crunch at the moment, and would really like to get some feedback on the above before proceeding further: have I missed anything in my reasoning? -- Jonathan Dataweaver Lang
Re: unusual invocants
HaloO, Jon Lang wrote: I have some more thoughts on this; but I'm on a time crunch at the moment, and would really like to get some feedback on the above before proceeding further: have I missed anything in my reasoning? I fully understand what you mean, I hope. But note that all instances of the class that does the two roles do both roles. So the object at hand can't select the dispatch target. So it has to come from the *environment* of the call. I consider this as very problematic because essentially you have to import that into the object so that it can be carried for a while---this is what you call wearing a role hat. We should keep the class dispatch as simple as possible and not mix in the environment of the call into the meaning of an object! Regards, TSa. -- The unavoidable price of reliability is simplicity -- C.A.R. Hoare Simplicity does not precede complexity, but follows it. -- A.J. Perlis 1 + 2 + 3 + 4 + ... = -1/12 -- Srinivasa Ramanujan
Re: unusual invocants
TSa wrote: Jon Lang wrote: I have some more thoughts on this; but I'm on a time crunch at the moment, and would really like to get some feedback on the above before proceeding further: have I missed anything in my reasoning? I fully understand what you mean, I hope. But note that all instances of the class that does the two roles do both roles. So the object at hand can't select the dispatch target. So it has to come from the *environment* of the call. I consider this as very problematic because essentially you have to import that into the object so that it can be carried for a while---this is what you call wearing a role hat. We should keep the class dispatch as simple as possible and not mix in the environment of the call into the meaning of an object! Usually, I'd agree with you - and even here, I'd say that if you can somehow resolve the dilemma without reference to the environment, that would be preferable. However, the only options that appear to be available without it are the two that Ovid outlined: rewrite one of the roles, or omit one of them. Both of these options become impractical once you account for the likes of CPAN: * Rewriting one of the roles may not be possible if the programmer doesn't own either of the offending roles; and even if it is possible, it likely involves a massive search-and-replace operation that isn't easily automated. * Omitting one of the roles is reasonable as long as you can guarantee that the overall concepts that the roles represent shouldn't be mixed (as is arguably the case for the traditional Dogwood example); but it's less than satisfactory when the only reason they can't be mixed is that each role's author made an unfortunate naming choice for one of the methods. My proposal isn't perfect, either: if an established routine fails to place the appropriate hat on the schizophrenic object, the environment-based disambiguation won't DWIM and the programmer will be forced to explicitly resolve the conflict before passing the object into that routine. Still, I don't get the same sense of it being a potential show-stopper the way that I get from the alternatives. But, as always, the devil's in the details. Perhaps someone _can_ provide a means of rewriting one of the roles in a way that won't break anything (the interface, the compiler's back, or the programmer's mind). And it may turn out that my proposal is unworkable once we start looking into the details of how it would be implemented. You mentioned that my approach involves importing a bit of the environment into the object for a while. How might this be done in a way that won't wreak havoc? One possibility would be to hide it away as a trait of WHAT, and then look for that trait when it comes time to disambiguate the schizo invocants: sub party(Drinking $x) { # $x.WHAT:role = Drinking ... $x.go_to_bar; ... # same as $x.go_to_bar:(Drinking:) } sub compete(Gymnast $x) { # $x.WHAT:role = Gymnast ... $x.go_to_bar; ... # same as $x.go_to_bar:(Gymnast:) } sub joke($x) { $x.WHAT:role is not set ... $x.go_to_bar; ... # can't be resolved without more information given Gymnast $x { # $x.WHAT:role = Gymnast ... joke($x); ... # the joke is about a Gymnast's bar. } Likewise, a method that originated in Drinking (such as .buy_beer) would set self.WHAT:role to Drinking, thus resulting in any call to .go_to_bar getting the right method from the class. (Thanks for the example, Jonathan; I hadn't thought of that rather obvious case.) If DrunkGymnast replaces .buy_beer or adds a new method that calls .go_to_bar, it won't automatically have knowledge of which hat it should be wearing; but that's OK, because it's expected to know about both hats, and can explicitly don the right one before (or when) calling the conflicted method. It's only those environments that can't be expected to know about the dual possibilities that concern me. What other problems might arise, and how might they be solved? -- Jonathan Dataweaver Lang
Re: unusual invocants
HaloO, On Wednesday, 21. October 2009 12:40:06 Mark J. Reed wrote: Rather than disallow the composition, I'd say that any class, role, or object that does both roles must override the method in question. The problem that Ovid posed needs to be resolved in the dispatch tables seen in certain methods of a class. So the solution is not overriding the offending method x but the calls to it in methods foo and bar! The interesting thing is that you have to dig that information out from the source of the roles T1 and T2. I doubt that a programmer making the dependency mistake also documents it ;) I've never seen a call graph of a library in any documentation. Which takes us back to Jon's branch of the thread: it would be nice to be able to declare such an override in a general way that will apply to any such composition that doesn't otherwise override it locally. But what should that declaration look like? Here is a direct syntax for the freeze feature of the paper: class C does T1 does T2 { freeze T1::x for foo; freeze T2::x for bar; method x {...} # for all other methods } The implementation is strait forward: on entry to foo and bar the dispatch table of the invocant is temporarily patched to contain the right x. After the call the original is restored. Heretic question: would it make sense to have a method registry for roles in CPAN? At least for a set of 'standard' modules that then allow arbitrary role combinations without conflict. Regards, TSa. -- The unavoidable price of reliability is simplicity -- C.A.R. Hoare Simplicity does not precede complexity, but follows it. -- A.J. Perlis 1 + 2 + 3 + 4 + ... = -1/12 -- Srinivasa Ramanujan
Re: unusual invocants
On Mon, Oct 19, 2009 at 11:47 PM, Jon Lang datawea...@gmail.com wrote: Because a method is part of a role, and ought to abide by the same terms by which the role abides. If Logging doesn't do Numeric, it shouldn't have any methods in it that won't work unless it does. 100% agreed. So what the OP wants to do is declare a method that is available on all those invocants - and only those invocatnts - which do all of roles X, Y, and Z. Granted, you can declare a new role XandYandZ that does X, Y, and Z, and define the method there, but that won't work on $foo unless you declare explicitly '$foo does XandYandZ' . The goal is to have the method show up no matter how $foo comes to do all three roles. This is an interesting idea. Currently, it doesn't work because there's no place for such a method to live, so perhaps there could be a way to declare a method space for arbitrary combinations of roles, a sort of meta-role. It's an odd duck, but it does sort of fall out of the multiple-dispatch semantics, which already let you base implementation chioce on arbitrary combinations of roles... -- Mark J. Reed markjr...@gmail.com
Re: unusual invocants
On Tue, Oct 20, 2009 at 2:32 PM, Mark J. Reed markjr...@gmail.com wrote: On Mon, Oct 19, 2009 at 11:47 PM, Jon Lang datawea...@gmail.com wrote: Because a method is part of a role, and ought to abide by the same terms by which the role abides. If Logging doesn't do Numeric, it shouldn't have any methods in it that won't work unless it does. 100% agreed. So what the OP wants to do is declare a method that is available on all those invocants - and only those invocatnts - which do all of roles X, Y, and Z. Granted, you can declare a new role XandYandZ that does X, Y, and Z, and define the method there, but that won't work on $foo unless you declare explicitly '$foo does XandYandZ' . The goal is to have the method show up no matter how $foo comes to do all three roles. This is an interesting idea. Currently, it doesn't work because there's no place for such a method to live, so perhaps there could be a way to declare a method space for arbitrary combinations of roles, a sort of meta-role. It's an odd duck, but it does sort of fall out of the multiple-dispatch semantics, which already let you base implementation chioce on arbitrary combinations of roles... Well, if you could put a where clause on your invocant you could do that... method m($invocant where { $_ ~~ X and $_ ~~ Y and $_ ~~ Z }: Int $a, Int $b) { ... } The STD.pm bot in #perl6 thinks where clauses on invocants are allowed, but Rakudo currently seems to completely ignore them. I'm not sure what the proper behaviour should be. Matthew
Re: unusual invocants
On 2009-Oct-20, at 7:55 am, Matthew Walton wrote: On Tue, Oct 20, 2009 at 2:32 PM, Mark J. Reed markjr...@gmail.com wrote: On Mon, Oct 19, 2009 at 11:47 PM, Jon Lang datawea...@gmail.com wrote: Because a method is part of a role, and ought to abide by the same terms by which the role abides. If Logging doesn't do Numeric, it shouldn't have any methods in it that won't work unless it does. 100% agreed. So what the OP wants to do is declare a method that is available on all those invocants - and only those invocatnts - which do all of roles X, Y, and Z. Granted, you can declare a new role XandYandZ that does X, Y, and Z, and define the method there, but that won't work on $foo unless you declare explicitly '$foo does XandYandZ' . The goal is to have the method show up no matter how $foo comes to do all three roles. Right. This is an interesting idea. Currently, it doesn't work because there's no place for such a method to live, so perhaps there could be a way to declare a method space for arbitrary combinations of roles, a sort of meta-role. It's an odd duck, but it does sort of fall out of the multiple-dispatch semantics, which already let you base implementation chioce on arbitrary combinations of roles... Yes, and while the answer could always be don't do that, the concept doesn't seem particularly strange or undesirable. Maybe rather than hide a Numeric method inside a Logging role where people wouldn't expect to find it, we could do it this way: role Numeric Logging { method log {...} } or something alone those lines. Well, if you could put a where clause on your invocant you could do that... method m($invocant where { $_ ~~ X and $_ ~~ Y and $_ ~~ Z }: Int $a, Int $b) { ... } I would expect $foo where {$_ ~~ X} and X $foo simply to be different ways of writing the same thing, but whatever works! -David
Re: unusual invocants
HaloO, On Tuesday, 20. October 2009 18:35:36 David Green wrote: So what the OP wants to do is declare a method that is available on all those invocants - and only those invocatnts - which do all of roles X, Y, and Z. Granted, you can declare a new role XandYandZ that does X, Y, and Z, and define the method there, but that won't work on $foo unless you declare explicitly '$foo does XandYandZ' . The goal is to have the method show up no matter how $foo comes to do all three roles. Right. I have difficulty seeing the need for a method here. The distinguishing feature of a method is the access to the private data of an object that can hardly be granted by doing the three roles X, Y and Z. After all there's no unique implementation of these roles! Perl 6 is a hybrid language as far as dispatch is concerned. There is the class based method dispatch that I call slot dispatch because the usual implementation is to have the objects carry a ref to a slot table. The other dispatch is the type based MMD. Unfortunately this also goes by the name of method. This is because other languages use classes as types and conflate the two dispatch regimes that Perl 6 clearly separates. There used to be fail-over from class dispatch to MMD but this didn't work---even though I forgot what the exact problems were ;) So in the end the only problem is that the calling conventions of $object.method versus method($object) are not interchangeable. But it makes the priority clear. In the $object.method case the object is the primary concept. We think of it as the object doing something. In many cases to its own state. In the method($object) case the method is the primary concept. The object influences how it is done or what is the output. The method can of course call mutating methods on the object but this is a secondary concern. Regards, TSa. -- The unavoidable price of reliability is simplicity -- C.A.R. Hoare Simplicity does not precede complexity, but follows it. -- A.J. Perlis 1 + 2 + 3 + 4 + ... = -1/12 -- Srinivasa Ramanujan
Re: unusual invocants
On Tue, Oct 20, 2009 at 5:35 PM, David Green david.gr...@telus.net wrote: I would expect $foo where {$_ ~~ X} and X $foo simply to be different ways of writing the same thing, but whatever works! Yes, but the where clause lets you test against multiple types at once. They don't participate in multiple dispatch in the same way though. Whether it should be possible to attach a where clause to an invocant appears to be unresolved.
Re: unusual invocants
On 2009-Oct-19, at 5:50 pm, Jon Lang wrote: In Aiasing methods in CPAN roles, David Green wrote: I don't want my special log() method to work only for other types that explicitly do NumLog; I want it to work for any type that directly does Numeric does Logging. But if Logging doesn't do Numeric, why should it be expected to provide a method that assumes that it does? Well, I don't want all objects that do Logging to do Numeric; I just want to have custom methods for those that do happen to do both. I could declare a sub log(Numeric Logging $x) that would work when its arg does both, but it has to be called like a sub, not a method. If I can put ad hoc compound types into a signature, e.g. foo(Numeric Logging) instead of foo(NumLog), then why shouldn't it be possible to define a method that way? Or conversely, should compound types in signatures be disallowed, and forced to use NumLog/whatever also? -David
Re: unusual invocants
David Green wrote: Jon Lang wrote: In Aiasing methods in CPAN roles, David Green wrote: I don't want my special log() method to work only for other types that explicitly do NumLog; I want it to work for any type that directly does Numeric does Logging. But if Logging doesn't do Numeric, why should it be expected to provide a method that assumes that it does? Well, I don't want all objects that do Logging to do Numeric; I just want to have custom methods for those that do happen to do both. ...which strikes me as a perfect argument for putting those methods in a role that does both. If I can put ad hoc compound types into a signature, e.g. foo(Numeric Logging) instead of foo(NumLog), then why shouldn't it be possible to define a method that way? Or conversely, should compound types in signatures be disallowed, and forced to use NumLog/whatever also? Because a method is part of a role, and ought to abide by the same terms by which the role abides. If Logging doesn't do Numeric, it shouldn't have any methods in it that won't work unless it does. -- Jonathan Dataweaver Lang