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