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 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???
Huh? You just use C<wantarray> (or preferrably the proposed C<want>) to
determine the calling context. Exactly as you do in normal subroutines.
> 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.
No more dangerous, I believe, than the existing aliasing behaviour of @_.
> 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 behaviour completly
> makes it unstable.
Obviously, I disagree. :-)
Damian