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

Nitpick my Perl6 - parametric roles

13 views
Skip to first unread message

Sam Vilain

unread,
Sep 25, 2006, 2:58:26 AM9/25/06
to perl6-l...@perl.org
Anyone care to pick holes in this little expression of some Perl 6 core
types as collections? I mean, other than missing methods ;)

role Collection[\$types] {
has Seq[$types] @.members;
}

role Set[::T = Item] does Collection[T] where {
all(.members) =:= one(.members);
};

role Pair[::K = Item, ::V = Item] does Seq[K,V] {
method key of K { .[0] }
method value of V { .[1] }
};

role Mapping[::K = Item, ::V = Item] does Collection[Pair[K,V]] {
all(.members).does(Pair) and
all(.members).key =:= one(.members).key;
}

role Hash[Str ::K, ::V = Item] does Mapping[K, V]
where { all(.members).key == one(.members).key }
{
method post_circumfix:<{ }> (K $key) of V|Undefined {
my $seq = first { .key == $key } &.members;
$seq ?? $seq.value :: undef;
}
}

role ArrayItem[::V = Item] does Seq[Int, V] {
method index of Int { .[0] }
method value of Item { .[1] }
};

role Array of Collection[ArrayItem]
where { all(.members).index == one(.members).index }
{
method post_circumfix:<[ ]> (Int $index) of Item {
my $seq = first { .index == $index } &.members;
$seq ?? $seq.value :: undef;
}
}

I'll take the feedback I get, and try to make a set of Perl 6 classes in
the pugs project that look and feel just like regular Perl 6 hash/arrays
but are expressed in more elementary particles.

Cheers,
Sam.

TSa

unread,
Sep 25, 2006, 10:38:09 AM9/25/06
to perl6-l...@perl.org
HaloO,

Sam Vilain wrote:
> Anyone care to pick holes in this little expression of some Perl 6 core
> types as collections? I mean, other than missing methods ;)

My comments follow.


> role Collection[\$types] {
> has Seq[$types] @.members;
> }

This is a little wrapper that ensures that collections have got
a @.members sequence of arbitrary type. This immediately raises
the question how Seq is defined.


> role Set[::T = Item] does Collection[T] where {
> all(.members) =:= one(.members);
> };

Nice usage of junctions! But how is the assertion of
uniqueness transported into methods that handle adding
of new values? In other words: I doubt that this comes
freely out of the type systems inner workings but is
more a statement of the intentions of a Set. Which leads
to the question when this where clause will be checked
or used to base MMD decisions.


> role Pair[::K = Item, ::V = Item] does Seq[K,V] {
> method key of K { .[0] }
> method value of V { .[1] }
> };
>
> role Mapping[::K = Item, ::V = Item] does Collection[Pair[K,V]] {
> all(.members).does(Pair) and
> all(.members).key =:= one(.members).key;
> }

I guess this is a typo and you wanted a where clause. The first
assertion of the members doing the Pair role should be guaranteed
by using Pair as the type argument when instantiating the Collection
role. Are you sure that the underlying Seq type handles the one and
two parameter forms you've used so far?


> role Hash[Str ::K, ::V = Item] does Mapping[K, V]

Will the Str assertion not be too specific for non-string hashes?

> where { all(.members).key == one(.members).key }
> {
> method post_circumfix:<{ }> (K $key) of V|Undefined {

Nice union type as return type. But isn't the type name Undef?

> my $seq = first { .key == $key } &.members;

Wasn't that @.members?

> $seq ?? $seq.value :: undef;

Ternary is spelled ?? !! now.


> }
> }
>
> role ArrayItem[::V = Item] does Seq[Int, V] {
> method index of Int { .[0] }
> method value of Item { .[1] }
> };

Here we see the two parameter version of Seq at work.


> role Array of Collection[ArrayItem]
> where { all(.members).index == one(.members).index }
> {
> method post_circumfix:<[ ]> (Int $index) of Item {
> my $seq = first { .index == $index } &.members;

Is this first there a grep-like function? Shouldn't it then
read 'first { .index == $index }, @.members'?


> $seq ?? $seq.value :: undef;
> }
> }
>
> I'll take the feedback I get, and try to make a set of Perl 6 classes in
> the pugs project that look and feel just like regular Perl 6 hash/arrays
> but are expressed in more elementary particles.

This might be very useful in future debates about these types. But
I think everything hinges on the basic Seq type.


Regards, TSa.
--

Miroslav Silovic

unread,
Sep 25, 2006, 11:22:19 AM9/25/06
to TSa, perl6-l...@perl.org
TSa wrote:
>
> > role Set[::T = Item] does Collection[T] where {
> > all(.members) =:= one(.members);
> > };
>
> Nice usage of junctions!
>

But buggy - one means *exactly* one. So for an array of more than 1
element, all(@array) never equals one(@array) - if they're all the same,
it's more than 1, otherwise it's 0.

all(.members) =:= any(.members) would also not work, as it will try to
match each member with some other or same member of the array. It will
always return true, in other words, as each element of the array is
equal to itself.

This leaves all(.members) =:= .members[0], possibly extra with
non-emptiness test.

Miro


TSa

unread,
Sep 25, 2006, 11:53:13 AM9/25/06
to Miroslav Silovic, perl6-l...@perl.org
HaloO,

Miroslav Silovic wrote:


> TSa wrote:
>> Nice usage of junctions!
>>
>
> But buggy - one means *exactly* one. So for an array of more than 1
> element, all(@array) never equals one(@array) - if they're all the same,
> it's more than 1, otherwise it's 0.

Doesn't all(1,2,3) == one(1,2,3) expand the all junction first?
So that we end up with 1 == one(1,2,3) && 2 == one(1,2,3)
&& 3 == one(1,2,3) which is true. In the case of duplicated
entries we get a false if the one-junction supports that.
That is, how does e.g. one(1,1,2) work? Is it failing for 1?

Regards,
--

Sam Vilain

unread,
Sep 25, 2006, 5:16:17 PM9/25/06
to TSa, perl6-l...@perl.org
TSa wrote:
>> role Collection[\$types] {
>> has Seq[$types] @.members;
>> }
> This is a little wrapper that ensures that collections have got
> a @.members sequence of arbitrary type. This immediately raises
> the question how Seq is defined.
> [...and later...]

> Are you sure that the underlying Seq type handles the one and
> two parameter forms you've used so far?

Ah, yes, a notable omission. I understood a Seq as a list with
individual types for each element, which are applied positionally. The
superclass for things like Pair.

Here's a quick mock-up of what I mean:

role Seq[ \$types ] {
submethod COMPOSE { # a la BUILD for classes
loop( my $i = 0; $i < $types.elems; $i++ ) {
# assumption: "self" here is the class
# we're composing to, and "has" is a class
# method that works a little like Moose
self.has( $i++ => (isa => $types.[$i]) );
}
}
my subset MySeqIndex of Int
where { 0 <= $_ < $types.elems };

method post_circimfix:<[ ]>( $element: MySeqIndex ) {
$?SELF.$element;
}
}

Seq is certainly interesting for various reasons. One is that it is a
parametric role that takes an arbitrary set of parameters. In fact,
it's possible that the type arguments are something more complicated
than a list (making for some very "interesting" Seq types); the above
represents a capture, but the above code treats it was a simple list.

>> role Set[::T = Item] does Collection[T] where {
>> all(.members) =:= one(.members);
>> };
>
> Nice usage of junctions! But how is the assertion of
> uniqueness transported into methods that handle adding
> of new values?

I haven't defined any mutable state yet; it's still "pure".

I'd expect mutability to be a different role, and a set of primitives
that work on this level by returning a new set with the changes made.
In fact the mutable Set could re-use those primitives, by just having a
Set as a member (that 'handles' the Set role methods), and automatically
changing that member each time a change is made, to point to the new Set
with the new contents.

Which all sounds very inefficient and scary until you realise what
happens inside generative GC VMs that support STM.

>> role Mapping[::K = Item, ::V = Item] does Collection[Pair[K,V]] {
>> all(.members).does(Pair) and
>> all(.members).key =:= one(.members).key;
>> }
>
> I guess this is a typo and you wanted a where clause. The first
> assertion of the members doing the Pair role should be guaranteed
> by using Pair as the type argument when instantiating the Collection
> role.

Well spotted; yes, that is superfluous.

>> role Hash[Str ::K, ::V = Item] does Mapping[K, V]
>
> Will the Str assertion not be too specific for non-string hashes?

Another assumption I make here is that there is a basic type "Hash",
which as its name suggests, is all about hashing strings, and that the
more general form is called "Mapping" where any object can be the
source, not just Str subtypes.

Thanks for your other nitpicks and comments - much appreciated!

Sam.

Ashley Winters

unread,
Sep 25, 2006, 10:28:21 PM9/25/06
to Miroslav Silovic, perl6-l...@perl.org
On 9/25/06, Miroslav Silovic <mi...@puremagic.com> wrote:
> TSa wrote:
> >
> > > role Set[::T = Item] does Collection[T] where {
> > > all(.members) =:= one(.members);
> > > };
> >
> > Nice usage of junctions!
> >
>
> But buggy - one means *exactly* one. So for an array of more than 1
> element, all(@array) never equals one(@array) - if they're all the same,
> it's more than 1, otherwise it's 0.

Yeah, that would've been cool. Are we left with asserting
C<all(.members »=:=« one(.members))>? That'd be pretty close to the
original elegance.

Ashley Winters

Darren Duncan

unread,
Sep 25, 2006, 11:02:36 PM9/25/06
to perl6-l...@perl.org

Unless I'm mistaken, you may be going about this the wrong way.

Within a system that already has an underlying
set-like type, the Junction in this case, a test
for uniqueness is (pardon any spelling):

all(@items).elements.size === @items.size

The all() will strip any duplicates, so if the
number of elements in all(@items) is the same as
@items, then @items has no duplicates.

Similarly, if @items is a list of Pair (such as
when you're implementing a Mapping), and you want
to assert that their keys are all distinct, then
this variation (pardon any spelling) would do it:

all(@items.map:{ .key }).elements.size === @items.size

Incidentally, a unique-key constraint on an RM
relation or table is like the latter as well, but
substitute "key" for the name(s) of the attribute
or column.

On the other hand, if you're using the above Set
to implement the Junction in the first place, we
can't use all/any/one/none etc in the definition.

-- Darren Duncan

Sam Vilain

unread,
Sep 26, 2006, 3:59:06 AM9/26/06
to Miroslav Silovic, TSa, perl6-l...@perl.org
Miroslav Silovic wrote:
> TSa wrote:
>
>>> role Set[::T = Item] does Collection[T] where {
>>> all(.members) =:= one(.members);
>>> };
>>>
>> Nice usage of junctions!
>>
>>
>
> But buggy - one means *exactly* one. So for an array of more than 1
> element, all(@array) never equals one(@array) - if they're all the same,
> it's more than 1, otherwise it's 0.
>
>

perl -MPerl6::Junction=one,all -le '@foo=qw(1 2 3 4); print "yes" if
(all(@foo) eq one(@foo))'
yes

Didn't test on pugs yet.

Sam.

TSa

unread,
Sep 26, 2006, 4:09:24 AM9/26/06
to perl6-l...@perl.org
HaloO,

Sam Vilain wrote:
> perl -MPerl6::Junction=one,all -le '@foo=qw(1 2 3 4); print "yes" if
> (all(@foo) eq one(@foo))'
> yes

But does it fail for duplicates? I guess not because junctions
eliminate duplicates and you end up testing unique values as
above. E.g. all(1,1,2) == one(1,1,2) might actually give the
same result as all(1,2) == one(1,2).

Regards,
--

Sam Vilain

unread,
Sep 26, 2006, 4:13:47 AM9/26/06
to Darren Duncan, perl6-l...@perl.org
Darren Duncan wrote:
> Unless I'm mistaken, you may be going about this the wrong way.
>
> Within a system that already has an underlying
> set-like type, the Junction in this case, a test
> for uniqueness is (pardon any spelling):
>
> all(@items).elements.size === @items.size
>
> The all() will strip any duplicates, so if the
> number of elements in all(@items) is the same as
> @items, then @items has no duplicates.
>

Perhaps, but then Junctions might not assume elements have equality or
identity operations defined. To do that you need to require this (and
which identity operator would you like to use today?), and that would
also have the side effect of making creating a junction an O(N^2) operation.

I think that the S06 definition needs re-wording. It's more like a Bag
than a Set AIUI.

Sam.

TSa

unread,
Sep 26, 2006, 6:55:58 AM9/26/06
to perl6-l...@perl.org
HaloO,

Sam Vilain wrote:
> Ah, yes, a notable omission. I understood a Seq as a list with
> individual types for each element, which are applied positionally.

I can understand that the type-checker can produce this type
for immutable sequences.


> The
> superclass for things like Pair.

Hmm, have to think about that. Tuple subtyping usually has the
reverse logic where a larger tuple is a subtype if the common
parts are subtypes.


> Here's a quick mock-up of what I mean:
>
> role Seq[ \$types ] {
> submethod COMPOSE { # a la BUILD for classes
> loop( my $i = 0; $i < $types.elems; $i++ ) {
> # assumption: "self" here is the class
> # we're composing to, and "has" is a class
> # method that works a little like Moose
> self.has( $i++ => (isa => $types.[$i]) );

I don't understand this line. First of all, why the increment?
Then what does the double pair achieve when given to the .has
method? I guess you want a numeric attribute slot.

> }
> }
> my subset MySeqIndex of Int
> where { 0 <= $_ < $types.elems };
>
> method post_circimfix:<[ ]>( $element: MySeqIndex ) {
> $?SELF.$element;

This is also unclear. Firstly there's no $?SELF compile time
variable anymore. Secondly you are using the invocant as method?
Shouldn't that be the sequence index for retrieving the numerical
attribute slot?

> }
> }
>
> Seq is certainly interesting for various reasons. One is that it is a
> parametric role that takes an arbitrary set of parameters. In fact,
> it's possible that the type arguments are something more complicated
> than a list (making for some very "interesting" Seq types); the above
> represents a capture, but the above code treats it was a simple list.

Can type parameters of roles be handled through normal variables as
your code does?


Regards,
--

Sam Vilain

unread,
Sep 26, 2006, 6:41:51 PM9/26/06
to TSa, perl6-l...@perl.org

Neither Pugs nor Perl6::Junction behaves like this.

pugs> for ([1,2,3], [1,1,2,3]) -> $x { my @x = @$x; my $all_unique = (
all(@x) == one(@x) ); print "{ $x.join(",") } became ", $all_unique; say
" (which is { $all_unique ?? "TRUE" !! "FALSE" })" }
1,2,3 became all(VJunc one(VBool True)) (which is TRUE)
1,1,2,3 became all(VJunc one(VBool False,VBool True),VJunc one()) (which
is FALSE)

Sam.

Darren Duncan

unread,
Sep 26, 2006, 7:36:46 PM9/26/06
to perl6-l...@perl.org
At 8:13 PM +1200 9/26/06, Sam Vilain wrote:

>Darren Duncan wrote:
> > Within a system that already has an underlying
>> set-like type, the Junction in this case, a test
>> for uniqueness is (pardon any spelling):
>>
>> all(@items).elements.size === @items.size
>>
>> The all() will strip any duplicates, so if the
>> number of elements in all(@items) is the same as
> > @items, then @items has no duplicates.
>
>Perhaps, but then Junctions might not assume elements have equality or
>identity operations defined.

As I recall, every type in Perl 6 has an equality and identity
operation defined because the Object superclass provides one. If
nothing else, the type's equality and identity are the same as =:=
and .WHERE.

>To do that you need to require this (and
>which identity operator would you like to use today?),

Junction and Set and Mapping keys and Hash keys etc would simply use
.WHICH and/or === when determining equality. That's one of the big
advantages in having such generic operators. Simple.

And don't bring up eqv or whatever, since keying should always be on
immutable things.

> and that would
>also have the side effect of making creating a junction an O(N^2) operation.

Not if the type uses a hash-like index internally; then creating a
junction (or set) is more like O(N).

>I think that the S06 definition needs re-wording. It's more like a Bag
>than a Set AIUI.

I don't know what part you are reading, but the list of types that I
see says that a Junction is "Sets with additional behaviours".

-- Darren Duncan

Sam Vilain

unread,
Sep 26, 2006, 8:13:58 PM9/26/06
to Darren Duncan, perl6-l...@perl.org
Darren Duncan wrote:
>> Perhaps, but then Junctions might not assume elements have equality or
>> identity operations defined.
>>
> As I recall, every type in Perl 6 has an equality and identity
> operation defined because the Object superclass provides one. If
> nothing else, the type's equality and identity are the same as =:=
> and .WHERE.
>

Ok, seems reasonable.

>> and that would
>> also have the side effect of making creating a junction an O(N^2) operation.
>>
> Not if the type uses a hash-like index internally; then creating a
> junction (or set) is more like O(N).
>

Oh, yes, good point.

>> I think that the S06 definition needs re-wording. It's more like a Bag
>> than a Set AIUI.
>>
> I don't know what part you are reading, but the list of types that I
> see says that a Junction is "Sets with additional behaviours".
>

We're on the same page. It's just not the way that any of the prototypes
have behaved to date.

Sam.

TSa

unread,
Oct 10, 2006, 10:08:34 AM10/10/06
to perl6-l...@perl.org
HaloO,

Darren Duncan wrote:
> Within a system that already has an underlying set-like type, the
> Junction in this case, a test for uniqueness is (pardon any spelling):
>
> all(@items).elements.size === @items.size
>
> The all() will strip any duplicates, so if the number of elements in
> all(@items) is the same as @items, then @items has no duplicates.

OK, but you are not using the 'set with additional behavior' of
junctions. How would that be spelled with a pure set?

set(@items).elements.size === @items.size

perhaps? This would nicely blend with the junction forms. Would
it even be the case that Set is a super role of the junctions?

Regards,
--

Darren Duncan

unread,
Oct 10, 2006, 5:53:41 PM10/10/06
to perl6-l...@perl.org
At 4:08 PM +0200 10/10/06, TSa wrote:
>HaloO,
>
>Darren Duncan wrote:
>>Within a system that already has an underlying set-like type, the
>>Junction in this case, a test for uniqueness is (pardon any
>>spelling):
>>
>> all(@items).elements.size === @items.size
>>
>>The all() will strip any duplicates, so if the number of elements
>>in all(@items) is the same as @items, then @items has no duplicates.
>
>OK, but you are not using the 'set with additional behavior' of
>junctions. How would that be spelled with a pure set?
>
> set(@items).elements.size === @items.size
>
>perhaps? This would nicely blend with the junction forms.

Yes, that is exactly how it would be spelled with a pure set. In
fact, your example is the more normal one, in that simply turning a
collection into a set removes duplicates. I used all() in my example
because that is the Junction equivalent of making a set that contains
all distinct members of @items. -- Darren Duncan

0 new messages