Re: About RFC 271: pre/post handlers
Damian Conway wrote: > Because, by specifying the handlers separately, all the messy > implementation details are automated and hidden. A single handler would > foist them back on the programmer. One of the main uses (possibly *the* > main use) of pre and post handlers is in Design-by-Contract OO > programming, where there can be dozens or hundreds of handlers required. > > And -- more compellingly -- because the inheritance semantics of > pre and post handlers are distinctly different. See: > Well, with those inheritance semantics, I believe the example : pre read_file { : flock $_[0], LOCK_EX; : } looses all its mean. Suppose the class whose method read_file has this pre-handler is inherited of one class that has another read_file pre-handler that only checks if the filename is not empty, or other silly stuff like this. Then the flock would never be called, because the superclass' handler got called and, as it didn't throw any exceptions, it don't need to call the class' handler. I agree that DBC is an important matter, and that a language like Perl should support this. But, as far as I know about DBC, it's about pre-conditions and post-conditions, not about pre-handlers. Of course, pre-conditions and post-conditions can be implemented with pre- and post-handlers, the way it's done in the section about Cat's and Tiger's, but I believe this cannot be generalized, because the inheritance semantics is only good in these cases. I mentioned the example about flock before, but I believe it's also valid to all the taxes-related examples you gave also. It's very dangerous to apply these kind of inheritance to things that have side effects that can be or not be called, depending only on the behaviour the superclass attaches to the same method! I think I didn't make myself clear with my approach, I may eventually rephrase it... Thanks, Branden.
Re: About RFC 271: pre/post handlers
Garrett Goebel wrote: > From: Branden [mailto:[EMAIL PROTECTED]] > > > > I was reading RFC 271 and thinking about this pre/post > > handler thing. Why instead of having 2 subs, one for > > pre and other for post condition, and having to deal > > with things as strange as $_[-1], why don't we > > have only one handler that calls the real sub? > > > > Instead of: > > > > : pre abc { do_pre_handler(@_); } > > : post abc { do_post_handler($_[-1]); } > > Because the {...} following pre/post name... are the _real_ subs. There is > no need for &do_pre_handler(@_) and &do_post_handler. On invokation of &abc, > both the anonymous subroutines for pre and post get passed the argument list > for the actual subroutine invokation, and $_[-1] which holds the eventual > return value. > > I suppose the $_[-1] is there to avoid adding a new keyword like &value. > This is all very similar to the Class::Contract module originally authored > by Damian Conway... which actually does allow you to use &value within a > post-condition instead of $_[-1]. > I'm sorry, I didn't express myself well. When I wrote do_pre_handler and do_post_handler I wanted to write a generic sub with statements, only didn't have any statements in mind. For example: suppose sub next_temp calculates a new temperature from an old temperature sub next_temp ($$) { my $this = shift; my $temp = shift; return $this->expression_with($temp); } Suppose it only handles Celsius, and we want to be able to handle Fahrenheit and Kelvin also. We could do it by: pre next_temp { my $temp = $_[1]; if ($temp =~ /^(\d+)F$/) { # Fahrenheit $temp = (($1 - 32) * 5) / 9; } elsif ($temp =~ /^(\d+)K$/) {# Kelvin $temp = $1 - 273; } elsif ($temp !~ /^(\d+)C?$/) { # Celsius die "Invalid temperature format: $temp"; } $_[1] = $temp; } Well, that's OK, right? Now suppose you're inheriting it from a class that has a pre-handler for next-temp that states: pre next_temp { if ($_[1] < -5) { die "Too cold!!!"; } elsif ($_[1] > 50) { die "Too hot!!!"; } } Well, now analyze the inheritance behaviour. The converting pre-handler would only be called if the temperature value were below -5 or above 50??? That's now DWIM for me!!! Another one: suppose I want to return the value in the same format that it was given to me. Damian told me that I could do it by: pre next_temp { my $temp = $_[1]; my $real_next_temp = (caller(0))[10]; my $result; if ($temp =~ /^(\d+)F$/) { # Fahrenheit $temp = (($1 - 32) * 5) / 9; $result = $real_next_temp->($temp); $_[-1] = ((9 * $result) / 5) + 32; } elsif ($temp =~ /^(\d+)K$/) {# Kelvin $temp = $1 - 273; $result = $real_next_temp->($temp); $_[-1] = $result + 273; } elsif ($temp =~ /^(\d+)C?$/) { # Celsius $_[-1] = $real_next_temp->($temp); } die "Invalid temperature format: $temp"; } Well, that's OK, althought I think the conversion back would be a post-handler thing. But that's a problem in the inheritance case, just as before, as also if there are other pre-handlers that would be called after this one, they wouldn't be called because this one would set $_[-1] and so return this value instead of calling the real sub!!! The $_[-1] thing is also a bad thing in that it hides the context the function was called. What if the function was called in a list context? $_[-1] would have an array reference? What if it had scalar context and returned an array reference the same way??? What I propose is, instead of having pre- and post- handlers, we have pre- and post- conditions that allow or don't allow a method to be called. That's what's needed for DBC. The inheritance semantics would be preserved, and so on, but allowing to change parameter list or even results is a dangerous thing to me. My proposal for these tasks (flock, temperature coversion) is to have a list of subs attached to each real sub. When the real sub is called, Perl would call each one of these subs, in order, passing them a pointer to the next sub, that is the real sub for the last pre-sub. For example: sub next_temp ($$) { my $this = shift; my $temp = shift; return $this->expression_with($temp); } Now I'll attach the pre_post_handler: sub next_temp_pre_handler { my ($this, $temp) = @_; my $next_sub_in_list = (caller(0))[10]; # or anything else... my $result; if ($temp =~ /^(\d+)F$/) { # Fahrenheit $temp = (($1 - 32) * 5) / 9; $result = $next_sub_in_list->($temp); return ((9 * $result) / 5) + 32; } elsif ($temp =~ /^(\d+)K$/) {# Kelvin $temp = $1 - 273; $result = $next_sub_in_list->($temp); return $result + 273; } elsif ($temp =~ /^(\d+)C?$/) { # Celsius return $next_sub_in_list->($temp); } die "Invalid temperature format: $temp"; } attach_pre_post_handler( \&
RE: About RFC 271: pre/post handlers (it's more than DBC)
From: Branden [mailto:[EMAIL PROTECTED]] > > For example: suppose sub next_temp calculates a new > temperature from an old temperature [...] > > Suppose it only handles Celsius, and we want to be able to > handle Fahrenheit and Kelvin also. We could do it by: if you have a procedure: sub next_temp ($$) { $_[0]->change_using($_[1]) } And you wanted to allow it to handle other temperature scales... I wouldn't change the object attribute's internal temperature scale representation. I would translate to and from it as necessary. pre next_temp { $_[1] = $self->convert($_[1]) if $_[1] =~ /([FKC])$/ } sub convert { ... } > Well, that's OK, right? Now suppose you're inheriting it from > a class that has a pre-handler for next-temp that states: > > pre next_temp { > if ($_[1] < -5) { > die "Too cold!!!"; > } elsif ($_[1] > 50) { > die "Too hot!!!"; > } > } This wouldn't be a problem with the code I've suggested. RFC 271 section on Inheritance of prefix handlers states: > This implies that any heritable prefix handlers inherited from > a base class are executed disjunctively with those heritable > handlers specified in the derived class. In practice this means > that the set of heritable prefix handlers from the base class(es) > is invoked within an eval and if none of them throws an exception, > that success suffices to satisfy the precondition constraint on > the method, in which case the heritable prefix handlers of the > derived class are not called (although the non-heritable handlers > are still invoked). If the eval terminates because some inherited > handler threw an exception, then the full set of prefix handlers > (heritable and non-heritable) for the derived-class are tried > instead. So... even with your code example, the exception thrown by the base pre, wouldn't matter if the derived pre is satisfied. This is slightly different from the implementation of Class::Contract where first the derived class' preconditions are checked, then failing that the ancestors. > Another one: suppose I want to return the value in the same > format that it > was given to me. Damian told me that I could do it by: > [...] > > Well, that's OK, although I think the conversion back would be a > post-handler thing. > > But that's a problem in the inheritance case, just as > before, as also if there are other pre-handlers that would > be called after this one, they wouldn't be called because > this one would set $_[-1] and so return this value instead > of calling the real sub!!! And you probably should use a post-handler. Damian's example was how to do it all with a single pre handler, if you wanted. I.e., just like your original example. TIMTOWTDI. Perhaps there will be a time or implementation change required when you _do_ want to avoid calling the real sub and subsequent post handlers? > The $_[-1] thing is also a bad thing in that it hides the context the > function was called. What if the function was called in a > list context? RFC 271 section on Prefix Handler Semantics states: > this (correctly) implies that a handler receives the same > information from caller and want (RFC 21) as its primary would How this is implemented... I have no idea. Personally, I find the use of $_[-1] confusing. In a pre when I first fetch $_[-1], is it the last argument or the placeholder for the return value? Or does the placeholder for the return value only come into existence when you attempt to store to it? That seems counter to splice(@_, -1, 'foo'). I'd much prefer a keyword like 'value' which could be used within the context of pre and post handlers. > What I propose is, instead of having pre- and post- handlers, > we have pre- and post- conditions that allow or don't allow > a method to be called. That's what's needed for DBC. That is what Class::Contract does. I think the pre/post handlers are a slightly more empowering and potentially obfuscating feature. Handlers can do more than conditionally check a procedure. They can affect what the real invocation sees, or usurp the procedure's invocation altogether. > What I mean is, DBC's pre- and post- conditions are VERY > important, and should be implemented, only shouldn't be > implemented in a way that leads to wrong stuff, like > assuming that this same pre/post handler stuff could be > used to flock files or change parameter values, or any of > such stuff. Its inheritance behavior completely makes it > unstable. I do kind of agree. DBC is very bondage and discipline... Loosening up the semantics of pre/post to allow them to "handle" the arguments a that procedure receives or the actual "implementation" invoked seems more "perlish", but at odds with the DBC philosophy. Hmm... I wonder if a strict 'handlers' pragma could be arranged? Limiting the generic handler mechanism into strictly DBC for a specific scope... However, there is a need for handlers. Note Jarkko's RFC 194: > This would open up a way towards very n
Re: About RFC 271: pre/post handlers
Branden writes: > > And -- more compellingly -- because the inheritance semantics of > > pre and post handlers are distinctly different. See: > > Well, with those inheritance semantics, I believe the example > > : pre read_file { > : flock $_[0], LOCK_EX; > : } > > looses all its mean. Nice play on words! ;-) > Suppose the class whose method read_file has this pre-handler RFC 271 doesn't ever suggest that the above example is part of a class. > is inherited of one class that has another read_file pre-handler > that only checks if the filename is not empty, or other silly stuff > like this. Then the flock would never be called, because the > superclass' handler got called and, as it didn't throw any > exceptions, it don't need to call the class' handler. Er, even if this *were* meant to be part of a class, that's not how the semantics are supposed to work (the RFC has a glaring mistake: derived pre-conditions are supposed to be tested *before*, not after inherited ones!) > I agree that DBC is an important matter, and that a language like > Perl should support this. But, as far as I know about DBC, it's > about pre-conditions and post-conditions, not about pre-handlers. > Of course, pre-conditions and post-conditions can be implemented > with pre- and post-handlers, the way it's done in the section about > Cat's and Tiger's, but I believe this cannot be generalized, > because the inheritance semantics is only good in these cases. I > mentioned the example about flock before, but I believe it's also > valid to all the taxes-related examples you gave also. It's very > dangerous to apply these kind of inheritance to things that have > side effects that can be or not be called, depending only on the > behaviour the superclass attaches to the same method! The mistake in the RFC (and thank-you for uncovering it!) has misled you here. You need to remember that pre/post conditions play a dual role in Perl that mirrors the dual role of subroutines. In non-OO packages, subroutines are called directly, and pre/post handlers are available to modify their behaviour. In OO packages (classes), subroutines are called polymorphically, so it's not necessary to use pre/post handlers to modify their behaviour (you just use polymorphism to do that). Instead, pre/post handlers can be used to *control* behaviour via their pre- and post-conditional roles. > I think I didn't make myself clear with my approach, I may eventually > rephrase it... No, I think you made it clear. > For example: suppose sub next_temp calculates a new temperature > from an old temperature > > sub next_temp ($$) { > my $this = shift; > my $temp = shift; > return $this->expression_with($temp); > } > > Suppose it only handles Celsius, and we want to be able to handle > Fahrenheit and Kelvin also. We could do it by: > > pre next_temp { > my $temp = $_[1]; > if ($temp =~ /^(\d+)F$/) { # Fahrenheit > $temp = (($1 - 32) * 5) / 9; > } elsif ($temp =~ /^(\d+)K$/) {# Kelvin > $temp = $1 - 273; > } elsif ($temp !~ /^(\d+)C?$/) { # Celsius > die "Invalid temperature format: $temp"; > } > $_[1] = $temp; > } > > > Well, that's OK, right? Now suppose you're inheriting it from a class that > has a pre-handler for next-temp that states: > > pre next_temp { > if ($_[1] < -5) { > die "Too cold!!!"; > } elsif ($_[1] > 50) { > die "Too hot!!!"; > } > } > > > Well, now analyze the inheritance behaviour. The converting pre-handler > would only be called if the temperature value were below -5 or above 50??? > That's now DWIM for me!!! Setting aside the error in the RFC, the point is that this isn't the way you use pre and post in an OO context. If you have a class with a next_temp that only handles Celsius, then you *derive* a new class with a new, polymorphic next_temp that handles the conversions (and probably delegated to SUPER::next_temp). > Another one: suppose I want to return the value in the same format that it > was given to me. Damian told me that I could do it by: > [snipped] > Well, that's OK, althought I think the conversion back would be a > post-handler thing. Well, now you're just being contrary. ;-) I suggested the all-in-one mechanism only to show that one could do it *your* way within the RFC's proposed mechanism. In a non-OO case, I too think the conversion back should be done by a post-handler! (In an OO case, the whole thing should be done by a derived polymorphic method.) > But that's a problem in the inheritance case, just as > before, as also if there are other pre-handlers that would be called after > this one, they wouldn't be called because this one w
RE: About RFC 271: pre/post handlers
From: Branden [mailto:[EMAIL PROTECTED]] > > Well, with those inheritance semantics, I believe the example > > Damian Conway writes: > : > : pre read_file { > : flock $_[0], LOCK_EX; > : } > > looses all its mean. Suppose the class whose method read_file has > this pre-handler is inherited of one class that has another > read_file pre-handler that only checks if the filename is not empty, > or other silly stuff like this. Then the flock would never be > called, because the superclass' handler got called and, as it > didn't throw any exceptions, it don't need to call the class' > handler. Yes... in order to make RFC 271 useful for both generic handlers _and_ conditional checks, you'd have to make it perform the ancestral pre-handlers and the derived ones, and only hiccup if they both throw exceptions. Or do it the way Class::Contract does and satisfy the derived class first, then failing that the ancestors. This just in "Damian Conway writes": > the RFC has a glaring mistake: derived pre-conditions are > supposed to be tested *before*, not after inherited ones!) Okay... good... nix. You'd want/need pre's called in derived-first order anyways to hide the messy implementation details of base classes. Damian Conway writes: > You need to remember that pre/post conditions play a dual role in > Perl that mirrors the dual role of subroutines. > > In non-OO packages, subroutines are called directly, and pre/post > handlers are available to modify their behaviour. Are you suggesting that using pre/post handlers in the context of methods "could not" or "should not" modify the method's @_ and implementation behavior? If "should not", then perhaps a strict 'handlers' pragma would be nice? If "could not", is there an RFC which proposes a new syntax to distiguish the declaration of a subroutine from a method? And what of translating the obscure Perl 5 case of procedures that can act as both subroutines and methods? (I believe CGI.pm and File::Spec do this). Garrett
RE: About RFC 271: pre/post handlers
From: Garrett Goebel > > This just in "Damian Conway writes": > > the RFC has a glaring mistake: derived pre-conditions are > > supposed to be tested *before*, not after inherited ones!) Hmm. Would it still skip performing the inherited pre handlers if the derived pre was satisfied? That is nice for Design-By-Contract... but what of Jarkko's RFC 194: Standardise Function Pre- and Post-Handling (http://dev.perl.org/rfc/194.html)? Wouldn't you want all the pre-handlers invoked all the way up? Even if you don't care about the results of those pre's which are conditions? Garrett
RE: About RFC 271: pre/post handlers
> > the RFC has a glaring mistake: derived pre-conditions are > > supposed to be tested *before*, not after inherited ones!) > > Okay... good... nix. You'd want/need pre's called in derived-first order > anyways to hide the messy implementation details of base classes. Yup. It was just a snafu on my part. And if it's the only one in the 28 RFCs I put in, I'll be *most* surprised. ;-) > > You need to remember that pre/post conditions play a dual role in > > Perl that mirrors the dual role of subroutines. > > > > In non-OO packages, subroutines are called directly, and pre/post > > handlers are available to modify their behaviour. > > Are you suggesting that using pre/post handlers in the context of methods > "could not" or "should not" modify the method's @_ and implementation > behavior? "Should not". "Could not" seems unnecessarily un-Perlish. > If "should not", then perhaps a strict 'handlers' pragma would be nice? Indeed. > If "could not", is there an RFC which proposes a new syntax to distiguish > the declaration of a subroutine from a method? I don't believe it should be "could not" (that would make p52p6 translation much harder) but, if it were, I would have thought that: sub name : method {...} would cover that. > > > the RFC has a glaring mistake: derived pre-conditions are > > > supposed to be tested *before*, not after inherited ones!) > > Hmm. Would it still skip performing the inherited pre handlers if the > derived pre was satisfied? Yes. > That is nice for Design-By-Contract... but what > of Jarkko's RFC 194: Standardise Function Pre- and Post-Handling > (http://dev.perl.org/rfc/194.html)? You need to distinguish the OO use of pre/post from the non-OO use. In the same way that we currently distiguish the OO use of subs from the non-OO. > Wouldn't you want all the pre-handlers invoked all the way up? Even > if you don't care about the results of those pre's which are > conditions? Not for methods. Methods don't need inherited pre-*handlers*, since you can already "wrap" them simply by providing an overriding method (one that delegates via SUPER::) in the derived class. Damian