On 10/28/05, Christopher D. Malon <[EMAIL PROTECTED]> wrote:
> On Oct 28, 2005, at 11:13 PM, Luke Palmer wrote:
> Trying to think through the VectorSpace example, and a slightly
> more complicated example (a Field), I'm starting to wonder whether
> roles need to be parameterized somehow. (Maybe they already are?
> This is one of my first p6 attempts, so bear with me...)
Oh yeah, roles can be parameterized.
role VectorSpace[Field ^f] {...}
role Field[AbelianGroup ^plus, Group ^times] {...}
I'm intentionally handwaving over some details that are important. See below.
> role Group should define a method "inverse()" whose implementation
> is up to the class. inverse(operation => '+') or inverse(operation
> => '*')
> would make perfect sense. On an object that does Group only one way,
> inverse() by itself would be okay, but it would be ambiguous
> on a Field object. I don't know whether it's better for the interpreter
> to produce a run-time error when a class implements a role in two ways
> and the parameter isn't supplied, or if one implementation of a role
> should
> take priority.
This is indeed the problem. A related problem is, let's say you have
a utility sub for groups:
# Group $x meaining an element of a group... that's a little strange
sub power(Group $x, Int $pow) {
given $pow {
when $_ < 0 { power(inverse($x), -$pow) }
when $_ == 0 { identity() }
when $_ > 0 { $x * power($x, $pow - 1) }
}
}
But what if you have a set that is a group under addition? That
doesn't work, since you're trying to multiply here.
sub power(Group[&infix:<*>] $x, Int $pow) {
# same body as before
}
That assumes that Group has one parameter: the operation under which
it is a group. Then you *bind* that parameter to a lexical infix:<*>,
which means that when you say * within the body of your subroutine,
you actually mean whatever operation the group defines.
However, we still have a problem when we give a Field to power. Which
group do we mean? One way to do this would be to pass an optional
parameter: an object describing the group:
sub power(Group $x, Int $pow, GroupDesc $desc = $x.desc) {
given $pow {
when $_ < 0 { power($desc.inverse($x), -$pow, $desc) }
when $_ == 0 { $desc.identity }
when $_ > 0 { $x * $desc.power($x, $pow - 1, $desc) }
}
}
But that's starting to take away from the readability of the code, and
it really takes away from the fact that Group is encoding the
structure.
There was a proposal in Haskell to have what they called "named
instances", which addresses these kinds of problems:
http://www.informatik.uni-bonn.de/~ralf/hw2001/4.pdf
Perlizing the proposal, we might have something like:
role Field[&infix:<+>, &infix:<*>] {
does Group[&infix:<+>] FieldPlus;
does Group[&infix:<*>] FieldTimes;
...
}
power(4, 3) (FieldPlus); # 12
power(4, 3) (FieldTimes); # 256
All group operations on a field would be on the Plus group if we're in
a FieldPlus context, and conversely, all group operations on the field
would be on the Times group if we're in a FieldTimes context.
Exploring this issue further could be important to Perl's power when
it comes to automatic dispatch.
Luke