On Thu, Feb 12, 2004 at 05:58:18PM -0800, Jonathan Lang wrote:
: Larry Wall wrote:
: > What I'm currently thinking about is a "does" predicate that tells you
: > if an object/class does a particular role completely.  If you pull
: > part of a role into a class, it returns false, because it doesn't do
: > the complete role.  However, if you use "like" instead, it returns a
: > number between 0 and 1 telling you what fraction of the role's methods
: > it uses.  So you can ask if your object is more like a Dog or a Tree.
: > 
: > Unless someone comes up with a better idea, of course.  Obviously
: > .does() is redundant with .like() == 1.  But it seems like a useful 
: > redundancy.
: 
: I'd rather they not be redundant.  Instead, C<does> would check to see if
: the role in question is ever directly referenced anywhere in the class
: definition (or in any of its parent class' definitions), whereas C<like>
: would check to see how many of the methods and/or attributes supplied
: and/or demanded by the role are available to the class.  Thus, a class
: that includes a C<bark> method but no C<Dog> role will return a not purely
: false value for the C<like> predicate, but would return false for the
: C<does> predicate.  This would tell you that the class can bark, but not
: neccessarily like a Dog.  Whereas if it answers true to C<does>, you know
: that when it barks, it will likely do so in the manner that a Dog does.  

I think that would be the wrong approach, or at least, not quite
the right approach.  If you're going to use all or part of the Dog
interface without the Dog implemention, you should still be relying on
the Dog role to define your interface.  To oversimplify, you should
still have your class explicitly "do" Dog to pull in the interface,
but then override all the methods as if Dog were a pure Interface.

That is oversimplified because if Dog subsequently changes, you could
end up pulling in the new part of the Dog implementation by accident,
and it might not play well with your implementation of the interface.

So perhaps we need a different word than "does" to indicate that
you want to include the Dog interface without including the Dog
implementation.  Perhaps we can do that with "is like(Dog)" or some
such if we don't want to Huffman code it shorter.  Then people who
believe only in Interfaces can use the same underlying system-defined
Roles without compromising their Java-bedeviled value system.  :-)

I think that your structural equivalence approach would be
harder to calculate and less likely to be correct.  Perhaps we could
retain .like for named equivalence, and have a .resembles for
structural equivalence.  But .resembles would have to do better
than merely comparing method names.  If it doesn't also compare
type signatures, it'll be demonstrably incorrect.  And if it does
compare type signatures, it'll be slower than .like, which can
rely on role names bound to method names at compile time.

Howsomever, if we do distinguish "does" from "like" at the caller end,
I'll go along with you far enough to agree that a .like predicate
should be more disjoint from a .does predicate, except insofar as
we should guarantee that $obj.like(Dog) >= $obj.does(Dog).  That is,
if .does == 1, then certainly .like == 1.  However, .does can drop
faster than .like, since it's constraints are stricter.

That's assuming that both .like and .does return 0 <= $x <= 1 in
numeric context.  In a Boolean context you only get 0 or 1, with
intermediate values truncated to 0, since you can't guaranteed
the interface if any methods have been overridden, and I suspect
people are more interested in guarantees than probabilities.

: Furthermore, I have my doubts about how useful a zero-to-one number would
: be as a return value for C<like>.  Personally, I'd rather get a junction
: of C<can> and C<has> predicate calls, which I could then query to find out
: exactly how the class is C<like> the role in question, and how it differs.
:  That is, 
: 
:    C<$x like $y> := 
:    C<any($x ?can? @methods($y)) or any($x ?has? @attributes($y))>
: 
: or
: 
:    C<all($x ?can? @methods($y)) and all($x ?has? @attributes($y))>
: 
: or maybe just
: 
:    C<$x ?can? @methods($y), $x ?has? @attributes($y)>

That's certainly possible, but also almost certainly inefficient.
In general, the approach people will actually use is to just assume
you can call the method you want, and catch the exception if it fails.
In that case, it almost doesn't matter how we define .like or .does.
It's a bit like people checking their method invocants for the correct
"isa", when they should just rely on the dispatch mechanism to find
the appropriate method, or fail.

Larry

Reply via email to