Re: A modest question (Damian, see last para please?)
Austin Hastings wrote: role Dog {must bark();} role Tree {must bark();} class crossPerson { method bark() {speak_sharply;} } class Trog does Tree does Dog { method bark() {bark_like_a_trog;} } multi sub purchase(Dog $mansBestFriend) {...} multi sub purchase(Tree $shrubbery) {...} multi sub purchase($noisemaker must bark()) {...} my crossPerson $jack; purchase $jack; my Trog $spot; purchase $spot; Which, if any, of the subs should be called in each case? Or should the compiler complain of duplicate definitions? $jack is a crossPerson, which absolutely does NOT have the Dog or Trog or Tree classes in its Cisa chain. If Dog and Tree are both inferred or inferrable classes, then the two multi declarations (Dog and Tree arguments) are identical. Thus, there's probably a warning or an error about conflict of inference for any call that's not passed an explicitly typed object. I would say that in the above examples, the calls to Cpurchase must throw a run-time exception, since in both calls, all three of the variants are equally close to their argument lists in parameter space. An exhaustive analysis of the variants could of course detect that the three signatures of the variants are all equivalent at compile-time, but I'm not sure Perl 6 will have (or indeed ought to have) such a robust static type inference mechanism on signatures. Mainly because that kind of inference easily becomes combinatoric in complexity as roles/classes become larger and as the number of parameters to each variant increases. Damian
RE: A modest question
On Thu, 2004-01-08 at 16:24, Jonathan Lang wrote: In this example, there's no difference between the Dog and Tree roles; however, this would almost certainly not be the case most of the time - at the very least, a class with a Dog role would have @.legs, while a class with the Tree role would have @.branches. However, if all that happens when you specify a demand for the Dog role in a signature is that the object must meet Dog's demands, then both crossPerson and Trog will be accepted. I would consider that a mistake. All that doing a role should imply is that somehow, that class understands the syntax *and* semantics of the methods of that role. Introspection can't reliably reveal whether $some_object.bark accesses a property (noun) or a method (verb). It's true that looking for @.legs versus @.branches could get you closer, but I'm not sure that it's been decided whether methods of a role should blissfully ignore all of the object's state. Besides, in a delegation situation, there could easily be some sort of magic that handles those attributes that the introspection mechanism might miss. -- c
RE: A modest question (Damian, see last para please?)
Jonathan Lang [mailto:[EMAIL PROTECTED] wrote: Austin Hastings wrote: Indeed. I like the idea of dynamic anonymous roles -- it's more behavioral than anything else. sub print_it ($thingie must stringify()) {...} Definitely gets down to the lowest level quickly, which is nice. Even nicer is the ability to use this sort of requirement as kind of an advanced signature: declare exactly what you're going to do. (In other words, your signature may say sub foo(Object $o) {...} because you want to accept anything in the hierarchy. But it's nice to extend it with sub foo(Object $o must stringify() must isa() must typeof()) {...} Valid, if wordy. Roles remain useful in that they provide a more concise way of handling this if you want it - if you've got a dozen routines that all Cmust isa() must typeof(), you might be better off defining a role that makes those demands, and then just use it. Sure, I agree. But when you want to specify exactly what you're doing (sort of like call-by-contract, I guess) you can get really detailed. When you have a dozen common cases, define an inferred role. But if any object with methods A, Q, and Z are going to be acceptable, why not? This kind of granularity does kind of imply a JavaScript-like ability to compose objects, too, no? (If you can compose requirements atomically, why not compose capabilities, too?) my $photon does Particle does Wave {...} = spark(); That's where Cbut comes in: my $photon but does Particle does Wave {...} = spark(); would be equivelent to something like class _anonymous_photon does Particle does Wave {...} my _anonymous_photon $photon = spark(); Is that ['but'] really necessary? Also: in the first of these two, would classof($thingie) actually have to have Stringify as a role, or would it be reasonable to instead say that classof($thingie) must meet Stringify's demands? The latter would require more work on the compiler's part, but would be considerably more flexible. I prefer the latter. I want to be able to compose requirements on the way. I certainly don't want to have to rewrite the core libraries (or extend them all) just to mix in an interface role that they already satisfy. In principle, I agree with you; in practice, it may not be workable. Verification should be just as easy for this as for a typed referenced parameter. I don't see why it wouldn't be workable. Perhaps Stringify $thingie requires that the Stringify role must actually be used, while something like $thingie like Stringify would only require that Stringify's demands be met? My thought would be that once you have an object in hand, you can do with it what you will. But all you get is the object. How would you handle the following: role Dog {must bark();} role Tree {must bark();} class crossPerson { method bark() {speak_sharply;} } class Trog does Tree does Dog { method bark() {bark_like_a_trog;} } multi sub purchase(Dog $mansBestFriend) {...} multi sub purchase(Tree $shrubbery) {...} multi sub purchase($noisemaker must bark()) {...} my crossPerson $jack; purchase $jack; my Trog $spot; purchase $spot; Which, if any, of the subs should be called in each case? Or should the compiler complain of duplicate definitions? $jack is a crossPerson, which absolutely does NOT have the Dog or Trog or Tree classes in its Cisa chain. If Dog and Tree are both inferred or inferrable classes, then the two multi declarations (Dog and Tree arguments) are identical. Thus, there's probably a warning or an error about conflict of inference for any call that's not passed an explicitly typed object. Given the strictness of the Crole behavior, I think that the consistent answer is to fail as soon as possible and report that the three purchase subs are in conflict with each other. (In the generally interesting case of what to do with the Dog/Tree multisubs, I think the right answer is to catch this either when class Trog is declared, or when Cpurchase $spot; is called. Comments from @Larry, especially Damian, would be nice.) =Austin
RE: A modest question
From: chromatic [mailto:[EMAIL PROTECTED] On Wed, 2004-01-07 at 00:43, Jonathan Lang wrote: Maybe as an alternative to role Stringify {must stringify();} sub print_it (Stringify $thingie) {print $thingie.stringify();} you might be able to say sub print_it ($thingie must stringify()) {print $thingie.stringify();} Hmm... there's a certain elegance to being able to specify one or two requirements directly in a signature. I'm not sure that works so well in practice. You're explicitly asking for a method with a particular name when you're ought to be asking for a method with a particular meaning. That is, if you said: method threaten ( $thingie must bark() ) { ... } passing in a Tree object would work, when what you really want something that does Doggish things, like a Dog, an Actor in a dog costume, or a RobotDog. Ahh, this is the classic Damian interpretation. Yes, passing in a Tree would satisfy the requirement. That's the intention. If we wanted to say that the requirement was for a Dog role, we'd say that. Instead, we're being very generic, and saying that anything with a bark() method is valid fodder for this. For example, sub bark_louder($it must bark()) { System::Audio::set_volume(+5); $it.bark; System::Audio::set_volume(-5); } Promoting role names to a position of typishness allows roles to express the semantics and context of method names that method names alone can't express uniquely. Yikes, now I sound like Larry. Sure, but you're missing the point. It's not methods instead of roles, it's methods when even roles are too abstract. =Austin
RE: A modest question
Austin Hastings wrote: Jonathan Lang wrote: Austin Hastings wrote: This kind of granularity does kind of imply a JavaScript-like ability to compose objects, too, no? (If you can compose requirements atomically, why not compose capabilities, too?) my $photon does Particle does Wave {...} = spark(); That's where Cbut comes in: my $photon but does Particle does Wave {...} = spark(); would be equivelent to something like class _anonymous_photon does Particle does Wave {...} my _anonymous_photon $photon = spark(); Is that ['but'] really necessary? Maybe; maybe not. How about this: When you apply Cdoes directly to an object, the result is that the object's class is changed to a singleton class identical to its previous class except that it also Cdoes the specified role. When you apply Cbut to an object, the result is that the object's class is changed to a singleton class that Cis its former class and Cdoes the specified role. That is, you use Cbut when you want the new role's methods to take precedence over the existing class methods, and you use Cdoes when you want the existing class methods to take precedence over the new role's methods. In the case of Cdoes, you also need to be able to specify any neccessary glue code (for conflict resolution, and/or to satisfy any demands of the new role that otherwise wouldn't be satisfied). Also: in the first of these two, would classof($thingie) actually have to have Stringify as a role, or would it be reasonable to instead say that classof($thingie) must meet Stringify's demands? The latter would require more work on the compiler's part, but would be considerably more flexible. I prefer the latter. I want to be able to compose requirements on the way. I certainly don't want to have to rewrite the core libraries (or extend them all) just to mix in an interface role that they already satisfy. In principle, I agree with you; in practice, it may not be workable. Verification should be just as easy for this as for a typed referenced parameter. I don't see why it wouldn't be workable. It isn't neccessarily because validation would be difficult; although we _are_ saying that validation would require you to do a point-by-point comparison of the object's available methods to the role's available methods rather than simply checking to see if the object's class has the role itself[1]. Just as important as this point is the fact that sometimes you'll actually want to validate against the literal presence or absence of the role itself in the object's class, rather than whether or not the class meets the role's demands - largely because roles can supply methods as well as demanding them, and by asking for that role you're actually saying that you want the class to use that role's semantics as well as syntax. Thus my suggestion that the default be that if you ask for a particular role in a signature, it will match any class that does that particular role; if you ask for something _like_ that particular role, it will match anything that meets that role's demands whether or not it actually does the role. So saying ($thing does Dog) would require that $thing Cdoes Dog; saying ($thing like Dog) would require that $thing satisfy Dog's demands. What you're asking for would still be available; just not through Cdoes. How would you handle the following: role Dog {must bark();} role Tree {must bark();} class crossPerson { method bark() {speak_sharply;} } class Trog does Tree does Dog { method bark() {bark_like_a_trog;} } multi sub purchase(Dog $mansBestFriend) {...} multi sub purchase(Tree $shrubbery) {...} multi sub purchase($noisemaker must bark()) {...} my crossPerson $jack; purchase $jack; my Trog $spot; purchase $spot; Which, if any, of the subs should be called in each case? Or should the compiler complain of duplicate definitions? $jack is a crossPerson, which absolutely does NOT have the Dog or Trog or Tree classes in its Cisa chain. Of course not; nothing will have Dog or Tree in their Cisa chain, because Dog and Tree are roles, not classes. If Dog and Tree are both inferred or inferrable classes, then the two multi declarations (Dog and Tree arguments) are identical. In this example, there's no difference between the Dog and Tree roles; however, this would almost certainly not be the case most of the time - at the very least, a class with a Dog role would have @.legs, while a class with the Tree role would have @.branches. However, if all that happens when you specify a demand for the Dog role in a signature is that the object must meet Dog's demands, then both crossPerson and Trog will be accepted. [1] It gets even messier because what you're really after is whether or not a specific syntax is legal for the object in question; it's entirely
Re: A modest question
chromatic [EMAIL PROTECTED] writes: On Tue, 2004-01-06 at 22:26, Austin Hastings wrote: So on the grand balance of utility, what are the metrics that traits are supposed to help improve? Two big ones: - naming collections of behavior that are too fine-grained to fit into classes cleanly - enabling finer-grained code reuse Consider a method that needs to print an object. You might require a String: sub print_it ( String $thingie ) { print $thingie; } Why does it have to be a String, though? What prevents it from working with anything that can stringify, besides the overly restrictive signature? What if you could say (the Perl 6 equivalent of): sub print_it ( does Stringify $thingie ) { print $thingie.stringify(); } That's both more general and something more specific. By asking for what you really want, you're not coding everyone else into a corner. In this particular case I prefer the Smalltalk thing of requiring all objects to have a Cprint_on($aStream) method and not even requiring that C$aStream satisfy the Stream role. But that's probably a matter of taste. -- Beware the Perl 6 early morning joggers -- Allison Randal
Re: A modest question
Piers Cawley wrote: Why does it have to be a String, though? What prevents it from working with anything that can stringify, besides the overly restrictive signature? What if you could say (the Perl 6 equivalent of): sub print_it ( does Stringify $thingie ) { print $thingie.stringify(); } That's both more general and something more specific. By asking for what you really want, you're not coding everyone else into a corner. In this particular case I prefer the Smalltalk thing of requiring all objects to have a Cprint_on($aStream) method and not even requiring that C$aStream satisfy the Stream role. But that's probably a matter of taste. Maybe as an alternative to role Stringify {must stringify();} sub print_it (Stringify $thingie) {print $thingie.stringify();} you might be able to say sub print_it ($thingie must stringify()) {print $thingie.stringify();} Hmm... there's a certain elegance to being able to specify one or two requirements directly in a signature. Also: in the first of these two, would classof($thingie) actually have to have Stringify as a role, or would it be reasonable to instead say that classof($thingie) must meet Stringify's demands? The latter would require more work on the compiler's part, but would be considerably more flexible. Perhaps Stringify $thingie requires that the Stringify role must actually be used, while something like $thingie like Stringify would only require that Stringify's demands be met? = Jonathan Dataweaver Lang __ Do you Yahoo!? Yahoo! Hotjobs: Enter the Signing Bonus Sweepstakes http://hotjobs.sweepstakes.yahoo.com/signingbonus
RE: A modest question
From: Jonathan Lang [mailto:[EMAIL PROTECTED] Piers Cawley wrote: Why does it have to be a String, though? What prevents it from working with anything that can stringify, besides the overly restrictive signature? What if you could say (the Perl 6 equivalent of): sub print_it ( does Stringify $thingie ) { print $thingie.stringify(); } That's both more general and something more specific. By asking for what you really want, you're not coding everyone else into a corner. In this particular case I prefer the Smalltalk thing of requiring all objects to have a Cprint_on($aStream) method and not even requiring that C$aStream satisfy the Stream role. But that's probably a matter of taste. Maybe as an alternative to role Stringify {must stringify();} sub print_it (Stringify $thingie) {print $thingie.stringify();} you might be able to say sub print_it ($thingie must stringify()) {print $thingie.stringify();} Hmm... there's a certain elegance to being able to specify one or two requirements directly in a signature. Indeed. I like the idea of dynamic anonymous roles -- it's more behavioral than anything else. sub print_it ($thingie must stringify()) {...} Definitely gets down to the lowest level quickly, which is nice. Even nicer is the ability to use this sort of requirement as kind of an advanced signature: declare exactly what you're going to do. (In other words, your signature may say sub foo(Object $o) {...} because you want to accept anything in the hierarchy. But it's nice to extend it with sub foo(Object $o must stringify() must isa() must typeof()) {...} This kind of granularity does kind of imply a JavaScript-like ability to compose objects, too, no? (If you can compose requirements atomically, why not compose capabilities, too?) my $photon does Particle does Wave {...} = spark(); Also: in the first of these two, would classof($thingie) actually have to have Stringify as a role, or would it be reasonable to instead say that classof($thingie) must meet Stringify's demands? The latter would require more work on the compiler's part, but would be considerably more flexible. I prefer the latter. I want to be able to compose requirements on the way. I certainly don't want to have to rewrite the core libraries (or extend them all) just to mix in an interface role that they already satisfy. Perhaps Stringify $thingie requires that the Stringify role must actually be used, while something like $thingie like Stringify would only require that Stringify's demands be met? My thought would be that once you have an object in hand, you can do with it what you will. But all you get is the object. So: (Anonymous) sub print_it($thingie must toString()) { print $thingie.toString(); } (Yclept) role Stringable { must toString(); } sub print_it(Stringable $thingie) { print $thingie.toString(); } Versus (Discrete) role Stringify { must toString(); method stringify() { return .toString(); } } sub print_it(Stringify $thingie) { print $thingie.stringify(); } The former cases don't add any methods to the objects method table, so an inferred object type is okay. The latter requires that a stringify() method be available, so you'll have to have an object that declared itself Cdoes Stringify at the outset. =Austin
RE: A modest question
Austin Hastings wrote: Jonathan Lang wrote: Maybe as an alternative to role Stringify {must stringify();} sub print_it (Stringify $thingie) {print $thingie.stringify();} you might be able to say sub print_it ($thingie must stringify()) { print $thingie.stringify();} Hmm... there's a certain elegance to being able to specify one or two requirements directly in a signature. Indeed. I like the idea of dynamic anonymous roles -- it's more behavioral than anything else. sub print_it ($thingie must stringify()) {...} Definitely gets down to the lowest level quickly, which is nice. Even nicer is the ability to use this sort of requirement as kind of an advanced signature: declare exactly what you're going to do. (In other words, your signature may say sub foo(Object $o) {...} because you want to accept anything in the hierarchy. But it's nice to extend it with sub foo(Object $o must stringify() must isa() must typeof()) {...} Valid, if wordy. Roles remain useful in that they provide a more concise way of handling this if you want it - if you've got a dozen routines that all Cmust isa() must typeof(), you might be better off defining a role that makes those demands, and then just use it. This kind of granularity does kind of imply a JavaScript-like ability to compose objects, too, no? (If you can compose requirements atomically, why not compose capabilities, too?) my $photon does Particle does Wave {...} = spark(); That's where Cbut comes in: my $photon but does Particle does Wave {...} = spark(); would be equivelent to something like class _anonymous_photon does Particle does Wave {...} my _anonymous_photon $photon = spark(); Also: in the first of these two, would classof($thingie) actually have to have Stringify as a role, or would it be reasonable to instead say that classof($thingie) must meet Stringify's demands? The latter would require more work on the compiler's part, but would be considerably more flexible. I prefer the latter. I want to be able to compose requirements on the way. I certainly don't want to have to rewrite the core libraries (or extend them all) just to mix in an interface role that they already satisfy. In principle, I agree with you; in practice, it may not be workable. Perhaps Stringify $thingie requires that the Stringify role must actually be used, while something like $thingie like Stringify would only require that Stringify's demands be met? My thought would be that once you have an object in hand, you can do with it what you will. But all you get is the object. How would you handle the following: role Dog {must bark();} role Tree {must bark();} class crossPerson { method bark() {speak_sharply;} } class Trog does Tree does Dog { method bark() {bark_like_a_trog;} } multi sub purchase(Dog $mansBestFriend) {...} multi sub purchase(Tree $shrubbery) {...} multi sub purchase($noisemaker must bark()) {...} my crossPerson $jack; purchase $jack; my Trog $spot; purchase $spot; Which, if any, of the subs should be called in each case? Or should the compiler complain of duplicate definitions? = Jonathan Dataweaver Lang __ Do you Yahoo!? Yahoo! Hotjobs: Enter the Signing Bonus Sweepstakes http://hotjobs.sweepstakes.yahoo.com/signingbonus
Re: A modest question
On Wed, 2004-01-07 at 00:43, Jonathan Lang wrote: Maybe as an alternative to role Stringify {must stringify();} sub print_it (Stringify $thingie) {print $thingie.stringify();} you might be able to say sub print_it ($thingie must stringify()) {print $thingie.stringify();} Hmm... there's a certain elegance to being able to specify one or two requirements directly in a signature. I'm not sure that works so well in practice. You're explicitly asking for a method with a particular name when you're ought to be asking for a method with a particular meaning. That is, if you said: method threaten ( $thingie must bark() ) { ... } passing in a Tree object would work, when what you really want something that does Doggish things, like a Dog, an Actor in a dog costume, or a RobotDog. Promoting role names to a position of typishness allows roles to express the semantics and context of method names that method names alone can't express uniquely. Yikes, now I sound like Larry. LarryOr maybe not./Larry -- c
Re: A modest question
On Tue, 2004-01-06 at 22:26, Austin Hastings wrote: So on the grand balance of utility, what are the metrics that traits are supposed to help improve? Two big ones: - naming collections of behavior that are too fine-grained to fit into classes cleanly - enabling finer-grained code reuse Consider a method that needs to print an object. You might require a String: sub print_it ( String $thingie ) { print $thingie; } Why does it have to be a String, though? What prevents it from working with anything that can stringify, besides the overly restrictive signature? What if you could say (the Perl 6 equivalent of): sub print_it ( does Stringify $thingie ) { print $thingie.stringify(); } That's both more general and something more specific. By asking for what you really want, you're not coding everyone else into a corner. Take Mail::SimpleList and Mail::TempAddress, for example. Both have classes that represent individual addresses or mailing lists. The appropriate parent class is Mail::Action::Address, which has the very basic data and properties that both subclasses share. Both simple lists and temp addresses should contain expiration dates, so both classes need some sort of behavior to implement that. When you throw another class into the mix, say, Mail::OneWayList, where there's no expiration (trust me, even though it's not on the CPAN yet), there's a problem. I'd like to share code between all three classes that represent aliases and Mail::Action::Address is the appropriate place to do that. I don't want to share *all* of the code, though, so I can't really put the expiration code in Mail::Action::Address. I *could* subclass Mail::Action::Address and make Mail::Action::Address::Expires and change the parent class of the temp address and the simple list classes, but that's kinda icky as it leads to yet another level in the class hierarchy. By turning expiration into a role, though, everything can extend Mail::Action::Address and only those classes that really need expiration can do it -- and they share the code. Contrived example? Maybe. Maybe not. Consider further James Fitzgibbon's Mail::Action::Role::Purge. James wanted to extend all Mail::Action subclasses to allow purging of expired addresses or lists. That's reasonable, but it's not something I wanted to add to Mail::Action because it doesn't know anything about expiration. So he made it a role and decorates expirable objects with the role and can do what he wants there. Again, the goals are specificity, genericity, and improved reuse. -- c