Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Edge case: incongruent roles

1 view
Skip to first unread message

Jonathan Lang

unread,
Oct 13, 2006, 6:35:01 PM10/13/06
to perl6language,
Jack is tasked to write a role "Foo". He does his job right, and
produces a role that perfectly produces the set of behaviours that
it's supposed to. Among other things, he defines a method
"&Foo::baz:()". He also writes a routine, "&foo:( Foo )", which calls
&Foo::baz.

Jill is tasked to write a role "Bar". Bar has little if anything in
common with Foo conceptually, but neither do the concepts clash with
each other. She also does her job right, and produces a role that
perfectly produces the set of behaviors that it's supposed to. Among
other things, she defines a method "&Bar::baz:()". She also writes a
routine, "&bar:( Bar )", which calls &Bar::baz.

These events occur entirely independant of each other.

Bob decides to create a class "Baz does Foo does Bar". Since the
underlying concepts of the roles do not clash, he should be able to.
However, because the independent designers of the roles both chose to
use &baz:(), a collision occurs when Bob tries this.

Here's Bob's dilemma: If he resolves the collision by having &Baz::baz
call &Foo::baz, then 'foo ( Baz )' will work properly, but 'bar ( Baz
)' will do strange things. Likewise, having &Baz::baz call &Bar::baz
will let 'bar ( Baz )' work properly, but will cause 'foo ( Baz )' to
behave strangely.

While Bob could write a &Baz::foo method that calls &Foo::baz or a
&Baz::bar method that calls &Bar::baz, 'foo ( Baz )' or 'bar ( Baz )'
wouldn't know to call &Baz::foo or &Baz::bar (respectively) instead of
&Baz::baz, and would still act strangely.

It is not realistic to expect Bob to write a single routine that works
properly in both cases, because the underlying concepts for Foo and
Bar are so different from each other.

Since Baz does both Foo and Bar, you cannot use type-checking to
resolve this dilemma.

The way things are now, it would appear that Bob's stuck. Short of
rewriting one of ( Foo and foo ) or ( Bar and bar ) to use a different
method name in place of baz, he can do nothing to resolve the
syntactic collision between Foo and Bar while continuing to let foo
and bar run properly.

--

Thoughts?

--
Jonathan "Dataweaver" Lang

Trey Harris

unread,
Oct 13, 2006, 7:11:53 PM10/13/06
to Jonathan Lang, perl6language,
In a message dated Fri, 13 Oct 2006, Jonathan Lang writes:
> Since Baz does both Foo and Bar, you cannot use type-checking to
> resolve this dilemma.

Why not? Why shouldn't this work:

my Foo $obj1 = getBaz(); # object is a Baz
$obj1.baz(); # Foo::baz is called

my Bar $obj2 = getBaz(); # object is a Baz
$obj2.baz(); # Bar::baz is called

my Baz $obj3 = getBaz(); # object is a Baz
$obj3.baz(); # Depends on Bob's implementation of ::Baz

All three objects happen to be Baz's, yes. But the client code doesn't
see them that way; the first snippet wants a Foo, the second wants a Bar.
They should get what they expect, or Baz can't be said to "do" either.

If methods are entirely dispatched *by name* ("duck typing"), then I
agree, we have a problem. But Perl 6 supposedly supports a) multiple
inheritance, b) multiple dispatch, c) traits, and d) DBC. I believe the
union of those requirements means that methods must be dispatched in a
more sophisticated way than just looking for a matching name.

(I know a guy who works in building maintenance and also does moonlighting
as a computer tech. In either of his two roles, he could be said to "do
windows". But what windows he does when someone asks him to do windows is
disambiguated by his current role.)

> The way things are now, it would appear that Bob's stuck. Short of
> rewriting one of ( Foo and foo ) or ( Bar and bar ) to use a different
> method name in place of baz, he can do nothing to resolve the
> syntactic collision between Foo and Bar while continuing to let foo
> and bar run properly.

Duck-typing would give you this dilemma, no question about it. But I
think Perl 6 is beyond duck-typing.

The dilemma only arises in the context of calling .baz on a *Baz*--and Bob
hasn't written the class Baz yet. The question remaining is just one of
disambiguation syntax and/or what happens when Bob chooses to do nothing
about disambiguation (compile-, compose- or instantiation-time error, or
some arbitrary default dispatch?).

Trey

>
> --
>
> Thoughts?
>
>

--

Jonathan Lang

unread,
Oct 13, 2006, 7:56:05 PM10/13/06
to Trey Harris, perl6language,
Trey Harris wrote:
> In a message dated Fri, 13 Oct 2006, Jonathan Lang writes:
> > Since Baz does both Foo and Bar, you cannot use type-checking to
> > resolve this dilemma.
>
> Why not? Why shouldn't this work:
>
> my Foo $obj1 = getBaz(); # object is a Baz
> $obj1.baz(); # Foo::baz is called
>
> my Bar $obj2 = getBaz(); # object is a Baz
> $obj2.baz(); # Bar::baz is called
>
> my Baz $obj3 = getBaz(); # object is a Baz
> $obj3.baz(); # Depends on Bob's implementation of ::Baz

The first two cases would also depend on Bob's implementation of Baz:
for dispatch purposes (single or multiple), the class' method takes
precedence over the role's method. This is there so that the class
can resolve disputes between roles, and so that the class can refine
role behavior as appropriate. Short-cirtuiting past the class' method
eliminates the latter option.

Also, even if the first two cases did work as you say, how would I get
$obj1 to call Bar::baz when it needs to?

> All three objects happen to be Baz's, yes. But the client code doesn't
> see them that way; the first snippet wants a Foo, the second wants a Bar.
> They should get what they expect, or Baz can't be said to "do" either.

In principle, I agree; that's how it _should_ work. I'm pointing out
that that's not how things work in practice according to the current
documentation. (Well, 99% of the time they do; that's why this is an
edge case.) If the first snippet asks for a Foo and gets handed a
Baz, it receives a Baz - and then (ideally) works with those portions
of Baz that behave like Foo. The problem is that roles that can be
used together in theory but which conflict in practice can lead to
cases where the snippet _thinks_ it's working with the Foo-like
portions when it really isn't.

> If methods are entirely dispatched *by name* ("duck typing"), then I
> agree, we have a problem. But Perl 6 supposedly supports a) multiple
> inheritance, b) multiple dispatch, c) traits, and d) DBC. I believe the
> union of those requirements means that methods must be dispatched in a
> more sophisticated way than just looking for a matching name.

This dilemma has nothing to do with dispatch entirely by name (which,
incidently, is _not_ "duck typing"): even the example that I gave
allowed for dispatch on the invocant (which includes multiple
inheritance and access to roles), and the problem wouldn't go away if
I put "multi" in front of the keywords, bringing MMD into play.

> (I know a guy who works in building maintenance and also does moonlighting
> as a computer tech. In either of his two roles, he could be said to "do
> windows". But what windows he does when someone asks him to do windows is
> disambiguated by his current role.)

Thanks for the example; this will make things more concrete.

role Janitor {
method do_windows() { #<code> }
method sweep_floors() { #<code> }
}
sub clean_up ( Janitor $joe ) { $joe.do_windows(); $joe.sweep_floors(); }

role Tech {
method do_windows() { #<code> }
method do_linux() ( #<code> }
}
sub program ( Tech $tom, Bool $boss_is_idiot ) {
if $boss_is_idiot { $tom.do_windows() } else { $tom.do_linux() }
}

class Moonlighter does Tech does Janitor {
method do_windows() { #<????> }
}

my Moonlighter $sam;
clean_up($sam); # calls Moonlighter::do_windows()
program($sam, :boss_is_idiot); # calls Moonlighter::do_windows()

--
Jonathan "Dataweaver" Lang

Larry Wall

unread,
Oct 13, 2006, 8:37:56 PM10/13/06
to perl6language,
On Fri, Oct 13, 2006 at 04:56:05PM -0700, Jonathan Lang wrote:
: Trey Harris wrote:
: >All three objects happen to be Baz's, yes. But the client code doesn't

: >see them that way; the first snippet wants a Foo, the second wants a Bar.
: >They should get what they expect, or Baz can't be said to "do" either.
:
: In principle, I agree; that's how it _should_ work. I'm pointing out
: that that's not how things work in practice according to the current
: documentation.

The current documentation already conjectures this sort of disambiguation
at S12:996, I believe.

Larry

Jonathan Lang

unread,
Oct 14, 2006, 10:56:24 AM10/14/06
to perl6language,

Right. That _almost_ takes care of the issue; the only part left
untouched is what happens if you have two methods that can only be
disambiguated by the invocant's role, and you aren't told what the
role is. For instance:

class DogTree does Dog does Tree {
method bark( Dog: ) { ... }
method bark( Tree: ) { ... }
}
my DogTree $x;

$x.bark();

In this example, what does the dispatcher do?

--
Jonathan "Dataweaver" Lang

Larry Wall

unread,
Oct 14, 2006, 11:27:09 AM10/14/06
to perl6language,
On Sat, Oct 14, 2006 at 07:56:24AM -0700, Jonathan Lang wrote:
: Right. That _almost_ takes care of the issue; the only part left

: untouched is what happens if you have two methods that can only be
: disambiguated by the invocant's role, and you aren't told what the
: role is. For instance:
:
: class DogTree does Dog does Tree {
: method bark( Dog: ) { ... }
: method bark( Tree: ) { ... }
: }
: my DogTree $x;
:
: $x.bark();
:
: In this example, what does the dispatcher do?

Never gets to the dispatcher if you write that, because you have to
write "multi method" in the class to share a short name like that.
The compiler's semantic analyzer will barf on such a name collision,
just as if you tried to redefine a sub. But we'll leave that aside
and take the "multi" as given.

If there's no good reason to choose one over the other, multi semantics
dictate that the most prudent course of action is to choose neither
and die of ambiguity. This is documented.

On the other hand, if you say one of:

$x.*bark();
$x.+bark();

then the dispatcher could arguably call them both. But we haven't
really talked about whether .* notation should extend to multi methods
within a single class. .* is intended primarily for calling one
method from each class, and if you take that view then the initial
call into the class succeeds but the redispatch into the class's
multi fails from ambiguity.

As usual, I can argue it both ways. Could probably even locate a
gripping hand or two if I tried a little harder...

Larry

TSa

unread,
Oct 17, 2006, 12:41:38 PM10/17/06
to perl6language,
HaloO,

Help me to get that right with a little, more concrete example.

my Num $a = 5; # dynamic type is Int
my Num $b = 4;

say $a/$b; # 1 or 1.25?

When we assume that Int is a subtype of Num and leave co- and
contravariance issues of container types out of the picture
and further assume the availability of dispatch targets :(Int,Int-->Int)
and :(Num,Num-->Num) in multi infix:</> then there is a conflict
between the static type information of the container and the dynamic
type of the values. And it resolves to the static container type
unless it is typed as Any, then the dynamic type is used. Right?

I know that the return type of / could be Num in "reality" but that
spoils the example. Sorry if the above is a bad example.

Regards, TSa.
--

TSa

unread,
Oct 17, 2006, 1:06:13 PM10/17/06
to perl6language,
HaloO,

TSa wrote:
> I know that the return type of / could be Num in "reality" but that
> spoils the example. Sorry if the above is a bad example.

Pinning the return type to Num is bad e.g. if you want multi targets
like :(Complex,Complex-->Complex). Should that also numerify complex
values when stored in a Num container? If yes, how?
--

Jonathan Lang

unread,
Oct 17, 2006, 7:27:44 PM10/17/06
to perl6language
TSa wrote:
> Pinning the return type to Num is bad e.g. if you want multi targets
> like :(Complex,Complex-->Complex). Should that also numerify complex
> values when stored in a Num container? If yes, how?

If at all possible, I would expect Complex to compose Num, thus
letting a Complex be used anywhere that a Num is requested.

--
Jonathan "Dataweaver" Lang

TSa

unread,
Oct 18, 2006, 5:34:46 AM10/18/06
to perl6language
HaloO,

Jonathan Lang wrote:
> If at all possible, I would expect Complex to compose Num, thus
> letting a Complex be used anywhere that a Num is requested.

This will not work. The Num type is ordered the Complex type isn't.
The operators <, <=, > and >= are not available in Complex. Though
I can imagine Num as a subtype of Complex. But I'm not sure if there
are cases where this breaks down as well---the square root function
comes to mind. With the root of a negative Num returning NaN or
Undef of Num which are technically Nums we could save the subtyping
relation.

In the end I think the two types Num and Complex both do a number
of common roles but don't have the same set of roles they do. E.g.
Comparable is available only for Nums. Basic arithmetic operations
like +,-,* and / are shared. The bad thing is that it will be
inconvenient to use. E.g. just using Num as parameter type in a sub
that does not use comparison should allow calling with a Complex even
though the nominal type is incompatible. IOW, the type inferencer
should determine a much more differentiated type than simple Num for
the parameter. How this type is then advertised I don't know.


Regards, TSa.
--

Jonathan Lang

unread,
Oct 18, 2006, 9:55:16 AM10/18/06
to perl6language
TSa wrote:
> Jonathan Lang wrote:
> > If at all possible, I would expect Complex to compose Num, thus
> > letting a Complex be used anywhere that a Num is requested.
>
> This will not work. The Num type is ordered the Complex type isn't.
> The operators <, <=, > and >= are not available in Complex.

They can be:

$A > $B if $A.x > $B.x | $A.y > $B.y;
$A < $B if $A.x < $B.x | $A.y < $B.y;

This also allows you to unambiguously order any arbitrary set of
complex numbers.

Pipe dream: it would be nice if something similar to the above
(faulty) code counted as valid Perl 6 logic programming.

> Though
> I can imagine Num as a subtype of Complex. But I'm not sure if there
> are cases where this breaks down as well---the square root function
> comes to mind. With the root of a negative Num returning NaN or
> Undef of Num which are technically Nums we could save the subtyping
> relation.

The square root function doesn't cause any problem, even if it dies
when given a negative Num. What's important as far as type-checking
is concerned is that it would be defined for both real and complex
numbers.

> In the end I think the two types Num and Complex both do a number
> of common roles but don't have the same set of roles they do. E.g.
> Comparable is available only for Nums. Basic arithmetic operations
> like +,-,* and / are shared. The bad thing is that it will be
> inconvenient to use. E.g. just using Num as parameter type in a sub
> that does not use comparison should allow calling with a Complex even
> though the nominal type is incompatible. IOW, the type inferencer
> should determine a much more differentiated type than simple Num for
> the parameter. How this type is then advertised I don't know.

As I've pointed out above, it's possible to define Complex such that
it does the full set of comparison operators; I'm pretty sure that
there's nothing that Num can do that Complex can't do as well or
better.

But for the sake of argument, let's say that comparison operators
weren't possible for complex numbers. For the purpose of type
checking, saying that role Complex does role Num except for the
comparison operators would mean that saying that "Complex.does(Num)"
returns false. Mind you, it could be a detailed "false" value - say,
one that provides a list of every method that Num requires that
Complex doesn't implement - but it would still fail the type-checking
test.

IMHO, it would be nice if there was some way for perl 6 to let a
programmer define a subset of an existing role's interface as a new
role (a reversal of the usual process, akin to "deriving" a
superclass from a subclass) for those situations where the role
designer didn't have the foresight to realize that a particular subset
of the role might be useful on its own, and the programmer isn't in a
position to hack the original designer's code. As I envision it, this
_would_ modify the original role - slightly - so that it now 'does'
the new subset, thus allowing type-checking that looks for the new
role to accept anything that 'does' the original role.

Other than expanding the range of types that the original role will
match, this should have absolutely no impact on the original role's
behavior. In the above example, this sort of thing would let you
create a subset of Num that doesn't include the comparison operators
(say, UnorderedNum), which Complex could then compose.

But we're probably best off leaving this sort of thing for Perl
6.2831853 or later (see Apocalypse 1).

--
Jonathan "Dataweaver" Lang

Nicholas Clark

unread,
Oct 18, 2006, 10:11:30 AM10/18/06
to Jonathan Lang, perl6language
On Wed, Oct 18, 2006 at 06:55:16AM -0700, Jonathan Lang wrote:
> TSa wrote:
> >Jonathan Lang wrote:
> >> If at all possible, I would expect Complex to compose Num, thus
> >> letting a Complex be used anywhere that a Num is requested.
> >
> >This will not work. The Num type is ordered the Complex type isn't.
> >The operators <, <=, > and >= are not available in Complex.
>
> They can be:
>
> $A > $B if $A.x > $B.x | $A.y > $B.y;
> $A < $B if $A.x < $B.x | $A.y < $B.y;
>
> This also allows you to unambiguously order any arbitrary set of
> complex numbers.

If I'm reading that correctly then there are values of $A and $B for which
$A > $B and $A < $B are simultaneously true. If so, that doesn't invalidate
your statement about ordering, but there will be different orders depending
on whether you order by < or >

Nicholas Clark

Jonathan Lang

unread,
Oct 18, 2006, 10:33:03 AM10/18/06
to perl6language
Nicholas Clark wrote:

> Jonathan Lang wrote:
> > They can be:
> >
> > $A > $B if $A.x > $B.x | $A.y > $B.y;
> > $A < $B if $A.x < $B.x | $A.y < $B.y;
> >
> > This also allows you to unambiguously order any arbitrary set of
> > complex numbers.
>
> If I'm reading that correctly then there are values of $A and $B for which
> $A > $B and $A < $B are simultaneously true.

Good point. What I meant to say was:

$A > $B if $A.x > $B.x | $A.x == $B.x & $A.y > $B.y;
$A < $B if $A.x < $B.x | $A.x == $B.x & $A.y < $B.y;

Or, in polar coordinates,

$A <= $B if pi/2 < ($A - $B).a < 3*pi/2;

With the other three comparisons defined relative to that. In effect,
anything that's anywhere to the east of A, as well as anything that's
due north of it, counts as being greater than it.

--
Jonathan "Dataweaver" Lang

Mark A Biggar

unread,
Oct 18, 2006, 11:38:52 AM10/18/06
to Jonathan Lang, perl6language
-------------- Original message ----------------------
From: "Jonathan Lang" <dataw...@gmail.com>
> TSa wrote:
> > Jonathan Lang wrote:
> > > If at all possible, I would expect Complex to compose Num, thus
> > > letting a Complex be used anywhere that a Num is requested.
> >
> > This will not work. The Num type is ordered the Complex type isn't.
> > The operators <, <=, > and >= are not available in Complex.
>
> They can be:
>
> $A > $B if $A.x > $B.x | $A.y > $B.y;
> $A < $B if $A.x < $B.x | $A.y < $B.y;
>
> This also allows you to unambiguously order any arbitrary set of
> complex numbers.
>
> Pipe dream: it would be nice if something similar to the above
> (faulty) code counted as valid Perl 6 logic programming.

That dosn't work. 1+2i < 2+1i then evaluates to (true | false) which is ambigious and can't be use to sort. Num."<=" as usually defined has certain properties: it's transitive: a<= && b<=c --> a <= c, it's reflexive: a<=a, it's anti-symetric: a<=b && b<=a --> a==b and it's total: one of a<=b or b<=a must hold. a<b is then defined to be a<=b && !(a==b). Note these are the properties needed sort a list of something. It is impossible to define "<=" on Complex so that is has all these properties and also that Nums compared using the Complex version of "<=" gives the same result as using the Num version of "<=". Note that you need this to work that way if you want Num to be a subtype of Complex.

--
Mark Biggar
ma...@biggar.org
mark.a...@comcast.net
mbi...@paypal.com

Jonathan Lang

unread,
Oct 18, 2006, 4:59:25 PM10/18/06
to perl6language
Mark Biggar wrote:

> Jonathan Lang wrote:
> > They can be:
> >
> > $A > $B if $A.x > $B.x | $A.y > $B.y;
> > $A < $B if $A.x < $B.x | $A.y < $B.y;
>
> That dosn't work.

Agreed. The above was written in haste, and contained a couple of
fatal syntax errors that I didn't intend. Try this:

multi infix:< <= > (Complex $A, Complex $B) {
$A.x < $B.x || $A.x == $B.x && $A.y <= $B.y
}

or

multi infix:< <= > (Complex $A, Complex $B) {
$A == $B || pi()/2 < ($A - $B).a <= 3*pi()/2 # if $.a is
normalized to 0..2*pi()
}

This is transitive, reflexive, and anti-symmetric. The underlying
rule for <= in English: anywhere to the west, or due south, or equal.

--
Jonathan "Dataweaver" Lang

Mark A Biggar

unread,
Oct 18, 2006, 7:25:02 PM10/18/06
to Jonathan Lang, perl6language

-------------- Original message ----------------------
From: "Jonathan Lang" <dataw...@gmail.com>

See the following.

http://www.cut-the-knot.org/do_you_know/complex_compare.shtml

0 new messages