Re: Why breaking can() is acceptable
by BrowserUk (Patriarch) on Apr 06, 2004 at 02:25 UTC
|
This sounds a little like using turn signals (indicators) on a car. The law might mandate it, and it's certainly not a bad thing to do, but driving such that you are commited to relying upon others doing so correctly is fraught with danger.
The problem with this type of 'rule', is it requires that everyone voluntarily comply with it, before it will ever be safe to benefit from it.
To extend the analogy a little further, it also suffers in the same way as turn signals in that it is nearly impossible to automate, because it requires you know where you are going before you get to the decision point; but we all have to visit strange cities, and we are all subject to late breaking clues and changes of direction.
The problem with turn signals, is that just as soon as you get comfortable with relying upon them, you encounter the driver that sets his signal left and then turns right.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] |
|
I think this is more like hiring a number of people and assigning each one a task, and then
following each one around to make sure they do it right. That's right: micromanagement.
For better or worse, i am with tilly on this one. If i need that kind of Bondage and
Discipline, i will code in Java and not Perl. (And i don't code Java ;))
| [reply] |
Re: Why breaking can() is acceptable
by dragonchild (Archbishop) on Apr 06, 2004 at 02:52 UTC
|
or even having behaviour that is customized per object.
I've got this in one of my production distributions. What did I do? I provided a can() method in that class that uses the same information that AUTOLOAD does. No duplication of information. It's all determined at runtime, anyways. AUTOLOAD needs to figure it out somehow based on the object, so can() should as well.
Personally, it's the module author's responsability to provide and support the implied interface that we are all accustomed to. If you do funky stuff with AUTOLOAD, then you are obligated to do the exact same funky stuff with can(). It's that simple.
I'm going to also stop in its tracks the counter-argument that UNIVERSAL::can() will be broken. Well, you know what? The only reason I can think of to call the UNIVERSAL:: version is for isa(), as a really cool version of ref(). If you're not sure if something is even an object, then why the heck are you asking if it provides a given method?!? I'm not sure I would be able to follow such obfuscation. It sure as hell better not be in my codebase cause it ain't gonna pass my code review.
(Personally, I don't like it when people call UNIVERSAL::isa(), either, cause it prevents me from doing cool things, like pretending I'm not really implemented as an ARRAY in order to fool HTML::Template into treating me like a SCALAR and stringifying me. Scalar::Utils::blessed() is a much better option, but blessed() didn't come into the core till 5.8.x. *shrugs* *makes a note to use blessed() more, now that he ranted about it*)
------
We are the carpenters and bricklayers of the Information Age.
Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose
| [reply] |
|
Personally, it's the module author's responsability to provide and support the implied interface that we are all accustomed to. If you do funky stuff with AUTOLOAD, then you are obligated to do the exact same funky stuff with can(). It's that simple.
Is it also the module author's responsibility to not break multiple inheritance?
The way that you described doing things underscores my point. If your funky class is in a later chain of inheritance with multiple inheritance, then you just broke can! Exactly what you said that it was your responsibility not to do!
Depending on how yhou implemented it, you may have broken can with single inheritance as well. (Particularly if the subclass tries to use AUTOLOAD. But one could argue that it is that subclass' responsibility to understand the class that it is overriding.)
Which is one of the reasons why I kept on saying UNIVERSAL::can. Overriding can in any other location leads to potential breakage. Personally I'm OK with that as long as you're clear on what breaks, and why. (I'm not a big fan of multiple inheritance, so I don't grieve when it dies. Others might.)
The dominant reason why I said it that way was to keep people from having to figure out whether I was talking about can as a noun or a verb in the same sentences. However the other issue was in the can of worms that I referred to if you try to override can locally. (Hrm, make that a verb or which noun? :-)
| [reply] |
|
tilly
First, I will say that if you are using multiple-inheritance in the presence of AUTOLOAD then you are already in trouble. Sure, you should be able to do it, but IMO thats being idealistic. Part of the difficulty of multiple inheritance is managing the dispatching of methods and name clashes, if your methods aren't even implemented (and handled with AUTOLOAD) your just asking for it.
As for how to implement local can without breaking it elsewhere, I don't see what the problem is. This (untested) code below (written waaaay past my bedtime) should serve as a stating point:
sub can {
# ------------------------------------
# this is the code tilly is talking about below
# sorry , it was late
# ------------------------------------
# my ($calling_package) = caller();
# if ($calling_package eq __PACKAGE__)
# ------------------------------------
# however, this is what I meant ...
my ($self, $method_name) = @_;
# if this is called with by an object specifically
# blessed into this __PACKAGE__, then we
# will handle this because of AUTOLOAD
if (ref($self) eq __PACKAGE__) {
return my_special_can_that_plays_nice_with_AUTOLOAD(@_);
}
else {
return $self->SUPER::can($method_name);
}
}
Now I am making the assumption that SUPER::can will work, but its alot easier to control where you inherit from then it is who inherits from you. If SUPER::can doesn't work, then do something that does work.
I will say again, can should work, period, end of story. If it doesn't work (because you used AUTOLOAD or even some symbol table madness) then you need to re-think your design.
-stvn | [reply] [d/l] [select] |
|
|
|
|
| [reply] |
Re: Why breaking can() is acceptable
by TimToady (Parson) on Apr 06, 2004 at 22:22 UTC
|
Interesting discussion. Perl 6 will not need to encourage people to define their own .can. Here's a paragraph from A12:
By the way, unlike in Perl 5 where .can returns a single routine
reference, Perl 6's version of .meta.can returns a "WALK" iterator
for a set of routines that match the name. When dereferenced, the
iterator gets fed to a dispatcher as if the method had been called
in the first place. Note that any wildcard methods (via delegation
or AUTOLOAD) are included in this list of potential handlers, so
there is little reason for subclasses to have to redefine .can to
reflect the new names. It does weaken the meaning of .can from
"definitely has a method of this name" to "definitely has one or
more methods in one or more classes that will try to handle this."
But that's probably closer to what you want, and the best we can do
when people start fooling around with wildcard methods under MI.
Also, we're making a clean separation between AUTOLOAD, which does "wildcard" methods, and
AUTODEF, which may only supply a definition for predeclared (stubbed) methods or subroutines. (The delegation syntax
also makes a clear distinction between those methods we know the
name of in advance and those we don't.) | [reply] [d/l] [select] |
|
I'm sure that I don't really understand the way that you described that, likely because I have not read the rest of A12 yet.
However if you haven't nailed that part of the specification down, this discussion has convinced me that it would be a definite improvement to have a wildcard decision on whether to handle a method. That is, instead of writing something like an AUTOLOAD, you would write a method named something like CAN, which would dynamically decide whether or not to accept this method, and if it does, returns the subroutine that does it.
In thinking about it, I've had trouble coming up with anything that you'd want to do with a reasonable AUTOLOAD which cannot readily be duplicated by a CAN that works like this. Furthermore while it is very hard to make multiple inheritance and AUTOLOAD cooperate, this CAN strategy cooperates with multiple inheritance rather smoothly. In fact it returns the best possible for can() to "definitely has a method of this name", even in the face of multiple inheritance and wildcard methods.
Unfortunately if a user wanted to implement this in Perl 6, as I understand your paragraph they would not be able to pass the extra information to can() that, "I can really tell you whether I'll do that". Which prevents can() from giving the answer that we probably really wanted it to give.
In fact I'm seriously thinking of writing a module named something like UNIVERSAL::AUTOLOAD_CAN which implements a UNIVERSAL::AUTOLOAD and UNIVERSAL::can that implements this solution in Perl 5. This would make it possible to get can(), AUTOLOAD and multiple inheritance to play together. (But only if you write no other AUTOLOADs!)
| [reply] |
|
I think this will work out ok if .meta.can, when it's looking for wildcarding classes, distinguishes classes that define their own .can from those that don't,
and only adds autoloaders to the end of the list that aren't shielded by a corresponding .can method. (The problems Perl 5 has with defining a .can method
in a class shouldn't arise in Perl 6, which tries to prefer "next" semantics to "super" semantics. At worst we'll
require people to say "next METHOD" at the end of each such .can method definition.)
| [reply] [d/l] [select] |
|
|
If a package/class has an AUTOLOAD sub, will you be able to decline to handle a method, but say "I don't want to handle this, but keep looking in the inheritance tree"? Say for some reason, you want your class to handle get_* methods, and the next class in ISA (or whatever the Perl 6 equivalent is), to handle set_* methods; nevermind how good/bad of a design this is :)
| [reply] |
|
Yes, you can always call "next METHOD" to pass the buck to the
next method in the list of candidates. Though an AUTOLOAD
is going to be pretty far down the pecking order, because Perl will prefer all declared methods over all wildcard methods, and autoloading is one form of wildcarding. So only an AUTOLOAD in a parent class (or a sibling class, since these are "next" semantics) is possible in the remaining list of candidates.
Though as for get_* and set_* methods, those would be non-standard in Perl 6. Perl's accessor methods will not be split into get and set methods. Rather, they'll be single methods that are lvaluable if you want to allow setting.
| [reply] |
|
| [reply] |
Re: Why breaking can() is acceptable
by stvn (Monsignor) on Apr 06, 2004 at 04:47 UTC
|
I'm going to agree with dragonchild, if you are going to do AUTOLOAD stuff then you should implement a version of can for your class. This is basic interface polymorphism (another of chromatic's pet peeves), you should be able to expect that inherited methods called on your object behave in a resonably obvious/intuative way or have a very good reason not too. Sometimes, this means not relying on the default implementation (UNIVERSAL::can) and instead write a custom implementation.
Part of the idea of using AUTOLOAD is to hide the implemenation (methods not being implemented) from the user of your class. If in doing so you break can (an expected inherited method of the UNIVERSAL base class) then you are breaking the object, and your code is incomplete.
I would even go so far to argue that if you cannot write a version of can that functions as a transparent replacement, you should re-think your use of AUTOLOAD in the first place.
I see no reason why it should be left broken or that you would need to make method stubs. It should work as expected, nothing more, nothing less.
-stvn
| [reply] [d/l] [select] |
|
I believe that getting a replacement can method to work absolutely correctly in all situations is far harder than it looks. I tried to explain some (not all!) of the issues in my root node. If you think that it is easier than I do, then post an interesting example of an AUTOLOAD and how you'd override can, and I'll try to demonstrate how - by your standards - your code is incomplete.
If the task proves difficult for you, right after you just read a description of some of the things that can go wrong, can we both agree that expecting people to consistently get it right is unrealistic?
| [reply] |
|
I never said it would be easy, I just said that you shouldn't break can for the sake of AUTOLOAD. But I am not a big fan of AUTOLOAD, while I do make (occasional) use of can so I would sooner throw away AUTOLOAD before I gave up can.
I will take you up on your challange, but with one change. I ask that you post and interesting example of AUTOLOAD, for which I will write a version of can. Partially because I don't like or use AUTOLOAD very much and to be honest would have a hard time coming up with an interesting example, and partially because I am not a fan of writing vaguely specified code for others to shoot holes into. Something like this is clearly complex, I do not deny that, but if you have a solid and well thought out implementation of AUTOLOAD I expect it would be possible to write a version of can to go with it.
-stvn
| [reply] [d/l] [select] |
|
|
|
|
The following snippet is taken from that example I alluded to above.
# This exists to provide object-specific can() functionality.
# 1) If this is an object, check to see if the method is an element na
+me.
# 2) If either is not true, redispatch to the parent's can() method.
+ sub can
{
(
ref($_[0]) &&
UNIVERSAL::isa( $_[0]->elements, 'HASH' ) &&
exists $_[0]->elements->{$_[1]}
)
||
(
$_[0]->SUPER::can($_[1])
);
}
sub AUTOLOAD
{
(my $name = our $AUTOLOAD) =~ s/.*::([^:]+)$/$1/;
my $self = shift;
unless ($name eq lc $name || exists $self->element_classes->{lc $n
+ame})
{
return eval "$self->SUPER::$meth(@_);";
}
$self->elements->{$name} = shift if @_;
$self->elements->{$name};
}
A few notes:
- This is a container object, providing a unified interface to parsing stuff. It acts as a record and the elements are the various columns. Each record may have different columns and the goal was to provide a way of allowing the user to call the column name as a method and get the column object.
- element_classes is a method which returns a hashref of name-class pairs. The name is the name of the column as provided to this object in the constructor. The class is the class of the column.
- elements is a hashref which actually contains the column objects in name-object pairs. It is guaranteed that the names from element_classes() will be identical to the names from elements().
This implementation has the added benefit of the fact that it's currently working in production. Now, nothing inherits from it (yet) and I do not use MI in production code, for the reasons well-cited elsewhere.
------
We are the carpenters and bricklayers of the Information Age.
Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose
| [reply] [d/l] |
Re: Why breaking can() is acceptable
by hardburn (Abbot) on Apr 06, 2004 at 13:57 UTC
|
sub AUTOLOAD
{
my $self = shift;
$self->{$AUTOLOAD} = shift if @_;
$self->{$AUTOLOAD};
}
The actual code could be more complex, such as checking $AUTOLOAD against a list of allowed attributes, but the idea of runtime handling of accessors/mutators is the same. If you must have autogenerated accessors/mutators, please use symbol table manipulation instead if at all possible:
# Could be wrapped in a BEGIN block if you wanted to
foreach my $field (qw/ foo bar baz /) {
*$field = sub {
my $self = shift;
$self->{$field} = shift if @_;
$self->{$field};
};
}
This is faster, is as memory-efficient as things get around Perl, and (IIRC) doesn't break can. Overall, it's even better to avoid spreading accessors/mutators all over your class design in the first place, but that's a seperate issue.
I understand the above isn't the only use of AUTOLOAD, but it's by far the most common. So if you're doing that, please stop and look for alternatives. Or better, fix your class design.
----
: () { :|:& };:
Note: All code is untested, unless otherwise stated
| [reply] [d/l] [select] |
|
And that is exactly what I was referring to with my comments on "typeglobbing closures instead of AUTOLOAD".
I also usually don't wrap it in a BEGIN block. In the context of a module, there is no real reason to wrap it in a BEGIN block, because the module itself is wrapped in an implicit BEGIN block. Otherwise I prefer to not try to specify BEGIN ordering unless absolutely needed, instead specify ordering of events as simple as I can arrange. (But that was an old discussion with tye.)
| [reply] |
|
Ok. I agree with you, for that instance. However, how do you handle syntactic-sugar methods that are instance-specific?
------
We are the carpenters and bricklayers of the Information Age.
Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose
| [reply] |
|
how do you handle syntactic-sugar methods that are instance-specific?
That is something that probably comes down to a case-by-case basis, so I don't want to give any hard rules on it. What I do want is to stop people from using AUTOLOAD to handle accessors/mutators when other solutions are available. This seems to be the most common use of AUTOLOAD, and it needs to stop. (Please direct flames twards tchrist :)
----
: () { :|:& };:
Note: All code is untested, unless otherwise stated
| [reply] [d/l] |
|
Re: Why breaking can() is acceptable
by Ovid (Cardinal) on Apr 06, 2004 at 22:37 UTC
|
For the most part, I agree with your assessment of the problem. However, I don't know that I agree with your solution because your solution attempts to deal with fundamental problems in Perl's OO model. Here are my thoughts.
First, programmers tend not to worry about these issues (and are less likely to encounter them) if they're building small systems. If they're building large systems, those programmers had durn well better know about those issues.
Second, I don't believe it is appropriate for programmers to attempt building large systems without a test suite. A good test suite will nail problems like this. If you find a bug, you add a test. This might seem to be irrelevant to your concerns, but I've found that with tests, while I write better code, I also worry less about constructs that I previously would have avoided due to apparent fragility.
Third, these issues are, of course, exacerbated when multiple inheritance is used, but programmers should usually be be delegating instead of inheriting. If UNIVERSAL::AUTOLOAD is searching a huge inheritance heirarchy, I submit that the system probably has some design issues. Scalability in Perl should usually not be achieved via complicated inheritance heirarchies. That's begging for trouble. Abigail's inside out objects can help, as can lexically scoped subs for private methods (my $sub = sub foo{};) and traits, but the reality is, Perl's OO has some quirks that people should be paying attention to. can seems to be pretty far down the list.
| [reply] |
Re: Why breaking can() is acceptable
by Beechbone (Friar) on Apr 06, 2004 at 08:21 UTC
|
I think this discussion is pointless. You either provide an interface to your module---then you decide if it supports can()---or you implement an interface another module expects---then the author of that module decided if can() is needed. That's it.
Implementing a working can() isn't that hard. I'd say this should work (ATTN: untested, WILL contain syntax errors):
sub can {
my ($self, $what, $code) = (@_[0,1], undef);
return $code if $code = $self->UNIVERSAL::can($what);
return $code if $code = $self->_can($what);
foreach my $superclass (@ISA) {
return $code if $code = eval "\$self->${superclass}::can(\$wha
+t)";
}
return undef;
}
sub _can { # aka _load()
my ($self, $what) = @_;
if ($what eq 'foo') {
return sub {'foo'};
} else {
return undef;
}
}
sub AUTOLOAD {
my ($self, $code) = ($_[0], undef);
goto &$code if $code = $self->can($AUTOLOAD);
die "oops";
}
PS: This even provides a working way for using AUTOLOAD with multi-inheritance, I'd say.
| [reply] [d/l] |
|
If everyone had your attitude, then I wouldn't have seen a point in having the discussion either. The issue is that there are people who think that any module which decides that can() shall not be supported is broken. Since you apparently agree with me that breaking can() is OK, we would have had little to discuss.
As for the solution that you provided, it tries to do a good job, but still has several subtle issues with it. First of all, and glaringly, your $AUTOLOAD always includes the package name and so will fail to do any kind of inheritance. That can (and should) be readily fixed. Secondly your AUTOLOAD fails (uninformatively) if you accidentally call an unimplemented function procedurally in any class that inherits from it. Thirdly if you appear partway up the class hierarchy in a multiple inheritance scheme, you will implicitly prune the class hierarchy for AUTOLOADed stuff to that hierarchy, contrary to what you claim about it being a working way for using AUTOLOAD with multiple inheritance. That is a common AUTOLOAD problem, your can matches what your AUTOLOAD does. (And it is a common multiple inheritance problem, people tend to think about the cases where they are at the bottom or top of the hierarchy, and don't think through being in the middle of the inheritance tree.) And finally it is worth noting that anyone who tries to do what I suggested with a UNIVERSAL::AUTOLOAD to allow can() and AUTOLOAD to play together will find that things implemented with their AUTOLOAD override yours. (Ironically this is probably a good thing.)
| [reply] |
What is this can() and why is breaking it (un)acceptable?
by jonadab (Parson) on Apr 06, 2004 at 14:29 UTC
|
Tilly, your arguments make sense to me, but I'm
previously unfamiliar with this UNIVERSAL::can;
I've never _heard_ of it before, much less used it.
I've certainly heard plenty about AUTOLOAD and
understand some of the things it would be useful
for, though I've never used AUTOLOAD either. But
if I ever needed something AUTOLOAD could provide,
I might use AUTOLOAD, and up to this point I would
not have even thought about this can thing.
So I guess what I want to hear from the people who
are saying that nobody should ever break can is,
why is it important for every module to work with
can? For example, I have a module that we'll call
Net::Server::POP3. At this time, it doesn't
use AUTOLOAD and so probably doesn't break can, but
for the sake of argument let's say I was contemplating
using AUTOLOAD in the next release. Explain to me
why it's important for my module to work with can.
What important thing will users of my module need
but be lacking if it doesn't?
;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}}
split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
| [reply] |
|
Quite simply, all classes in perl inherit from UNIVERSAL. UNIVERSAL implements can. Your class then has an obligation (IMO of course) to provide a working version of can in its interface. If you break aspects of your base class in your subclass you are doing bad OO and defeating the whole purpose of re-use through inheritance.
-stvn
| [reply] [d/l] [select] |
|
Your class then has an obligation (IMO of course) to provide a working version of can in its interface.
If we are talking from an OO purity point of view, I would agree. Anything that breaks inheritance is evil and must be avoided.
OTOH, a lot of people agree that multiple inheritance is needed in some cases, but there is no good way to implement it. How do you handle dimand inheritance? In what order do you call destructors (something that's been an issue on the Squawks of the Parrot blog of late)? There's no good answer, and an awful lot of bad ones.
Abigail-II gave a great example in this thread of how easy it is to break inheirtance in Perl (one which I hadn't considered before). If you implement your class as a hashref, all your parents and subclasses also must use a hashref. Which might be the most common case, but it isn't the only one.
Observation: this thread is a case study in why Perl's object system is hacked on.
----
: () { :|:& };:
Note: All code is untested, unless otherwise stated
| [reply] [d/l] |
|
Quite simply, all classes in perl inherit from UNIVERSAL. UNIVERSAL implements can.
This is an interesting statement. The index of my
Camel book (2nd ed) does not list can at all and
contains only one entry for UNIVERSAL, which points
to page 293, where I find the following quote
(emphasis mine):
If neither a method nor an AUTOLOAD routine
is found in @ISA, then one last, desperate try is
made for the method (or an AUTOLOAD routine) in the
special predefined class called UNIVERSAL.
This package does not initially contain any
definitions (although see CPAN for some), but
you may place your "last-ditch" methods there.
This not only doesn't support your assertion but
seems on the face of it to directly contradict what
you were saying. How can UNIVERSAL implement can,
and require that all derived objects (i.e., all
objects) not break that, if UNIVERSAL does not
initially contain any definitions? Further,
NOTHING is said here about any obligations that any
module has to provide or support any particular
method.
Granted, what I have is not the latest edition, but
none of the reviews I have read of the third edition
have said anything about the new edition containing
important information about fundamental changes to
the language that every Perl programmer must know,
nor is it advertised that way by the author or by
the publisher. It's simply the next edition of the
book, no more.
I did see some reviews praising the addition of
numerous new examples, but nothing that seemed to
indicate to me that if I program according to the
second edition I'll break things. To be perfectly
honest, I've got a lot of things marked in my camel
(not least, little tabs on the sides of the pages
to mark where certain sections start so I can quickly
flip e.g. to the section on special variables), and
not wanting to redo all of that right away I was
going to wait for the fourth edition, which hopefully
will cover Perl6, before upgrading. Perhaps you
could quote me just the paragraph of the 3rd edition
that explains why every object is required to
support the can method.
update: In the light of morning,
the next paragraph seems harsh to me. I didn't
mean it that way. I'm not going to edit it out,
though, because the reply wouldn't make (as much)
sense then. (And chromatic, I recognize your
reputation, but you hadn't yet come out to say
anything in the thread when I wrote this.)
I'm interested in hearing about the merits of can,
the practical reasons why it's a useful thing for
modules to support, even if the module author does
not personally use it. I'm somewhat less interested
in hearing you just tell me (in so many words)
"This is just required". I might accept
that coming from someone who is on a first-name basis
with Larry's wife, but I am dubious as to what
authority you have to make
up requirements for all Perl modules to adhere to.
;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}}
split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
| [reply] |
|
|
|
I'm assuming that Net::Server::POP3 inherits from some basic Net::Server module that provides basic server functionality. Even if it doesn't, let's say that it does.
Now, let's say that there is some other server base module, called Net2::Server. It provides a very similar (but not identical) interface, but radically different innards. It's useful for different types of servers.
Now, let's say that I am using five servers, each an object of a class that either inherits from Net::Server or Net2::Server. I don't know which inherits from which. But, I do know that there is one interface difference I need to be aware of - foo() vs. bar(). So, I can do the following:
my $method = $server->can('foo') || $server->can('bar');
$server->$method( @args );
And I guarantee that my code will work, regardless of which baseclass the server inherits from.
------
We are the carpenters and bricklayers of the Information Age.
Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose
| [reply] [d/l] |
|
And you've just illustrated where I think that can() can be problematical.
Despite your claimed guarantee, you didn't guarantee that Net2::Server (or some subclass thereof) won't someday implement a method named foo() that does something radically different from bar(). And then your code will break.
The problem is that it is as much an abuse of an API to assume that a particular method will never be implemented as it is to use an internal one which may change. Also related is that it is a mistake to assume that what you'd think that foo() should mean will be what someone else will think. This is true even for such obvious values of foo() as a constant named PI. Is that a number or a Unicode character? This related mistake is a meta-problem with can().
Instead the right way for you to do that is to have some compatibility layer which guarantees that the interface that you want will be supported. That layer could be something as simple as a hash that says that method X is provided by package Y under name Z. Or you can actually implement a class which proxies the methods that you are interested in (incidentally giving you somewhere to put utility methods that one base class has and the other doesn't).
Now you don't have to worry about the API of Net2::Server changing on you, and you don't need to call can().
| [reply] |
|
|