David Green wrote:
> Jon Lang wrote:
>> David Green wrote:
>>> On the other hand, $dogwood.Dog::bark cannot be simplified by leaving out
>>> the "Dog::" because then it would be ambiguous.
>>
>> On the gripping hand, if we have a function "train(Dog $d)", then we can
>> safely assume that within the lexical scope of train, $d is supposed to be
>> treated as a Dog.  So within that lexical scope, it should be safe to leave
>> off the "Dog::".
>
> Yes; and then my question from last time is whether the sig (Dog $d)
> "soft-casts" the arg such that the non-doggy bits of $d still remain, e.g.
> if inside train() we call a function chop(Tree $t), chop() will
> unambiguously see the Tree-half of the original Dogwood object.  Or will it
> be "hard-cast" such that the non-doggy bits are simply lost?  (And if so,
> does an ordinary cast Foo($d) do the same thing, or is one "hard" and one
> "soft", etc.?)

Here, we need a bit of a clarification: are we talking roles or
classes?  Real example: Numeric is a role; Num is a class.  Both can
be used in signatures; but only classes can be used to create objects.
 That is, "my Num $x;" works; but "my Numeric $x;" doesn't.  As such,
you cannot coerce an object to a role; you can only coerce it to a
class that does that role.

And when passing parameters, you don't coerce the object at all.  You
smart-match the prospective object against the criteria provided by
the signature to determine whether or not it's acceptable.

...which is a long-winded way of saying that it would be like a "soft
cast": all of the object's capabilities remain intact after being
passed as a parameter; the only thing that would change would be that
the lexical scope inside the routine would show a preference for the
Dog-like features of the object.  If you asked for a Dog, it's
reasonable to assume that you were given a Dog.

And the way I see it working, this preference would only show up in
one very specific circumstance: namely, when the object in question
has multiple methods that are distinguished from each other by their
invocant types.  When in a lexical scope that shows a preference for
$dogwood to play the role of a Dog, a call to $dogwood.bark() would
result in MMD looking at &bark:(Dog $dogwood:) and &bark:(Tree
$dogwood:) and choosing the former.  When in a lexical scope where the
preference is for $dogwood as a Tree, it would resolve the decision in
favor of the latter.  And if neither or both are preferred roles for
$dogwood, it would fail on account of too much ambiguity.

Another clarification: there's a subtle but important difference
between "$dogwood.bark:(Dog:).()" and "$dogwood.Dog::bark()".  The
former calls a Dogwood method that has an invocant that does Dog; the
latter calls a Dog method.  That is:

    $dogwood.bark:(Dog:).(); # calls &Dogwood::bark:(Dog:)
    $dogwood.Dog::bark();    # calls &Dog::bark:()

And because of the flattening nature of role composition, the latter
doesn't work: after you have composed Dog and Tree into Dogwood,
objects that are based on Dogwood no longer have access to the methods
provided by Dog or Tree; they only have access to the methods that
Dogwood provides.  (Caveat: if Dogwood doesn't explicitly provide a
method corresponding to something found in Dog or Tree, it does so
implicitly.)  This is perhaps the most crucial difference between role
composition and class inheritance: once role composition is complete,
you can ignore the implementation details of the roles that were
composed; all that matters is the implementation of the role or class
into which they were composed.

> However, I expect that "my Dog $d = $dogwood" would strip out everything
> else, on the grounds that you explicitly requested a pure Dog object.
>  Otherwise you could have said "my $d = Dog($dogwood)" or maybe "my
> $dogwood.^WHAT $d = $dogwood" instead.

With "my Dog $d = $dogwood", $d is a Dog that was initialized using
values gleaned from $dogwood.

-- 
Jonathan "Dataweaver" Lang

Reply via email to