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