Jonathan Worthington: June 2008 Archives

Rakudo day came around again, and I pushed everything else that is keeping me busy these days aside and focused on getting more of Perl 6 implemented. This week there are a handful of new features; as is the norm of late, I have been continuing my focus on the object model and the type system.

I started off the day with some refactoring. I wanted to bring us more in line with STD.pm - the standard Perl 6 grammar - as well as parse some additional bits of syntax that we didn't parse before for some new things that are coming in the not too distant future. Getting us closer to STD.pm's grammar rules could have been done without that much shuffling around, but there was an action method that was more than long enough already lying behind it all. I didn't like the thought of making it even longer and harder to follow when I came to add to it in a week or two, and the re-arrangement of the grammar presented an obvious way to break the action method up into several methods. So, I did that and that bit of the codebase is now a bit cleaner, and ready for when I and others need to build more on top of it.

So, what's new? Probably the most major new feature this week is that you can now compose roles into an object at runtime, mix-in style. Here is an example from the tests to illustrate some of the semantics.

role R { method test { say 42 } }
class C { has $.x }
my $y = C.new(x => 100);
$y does R;
$y.test; # 42 (called method from role)
say $y.x; # 100 - we didn't destroy old attribute values

There is also some special syntax, implemented too, for initializing values of a role that have a single attribute. For example, imagine we wrote an Answer role:

role Answer { has $.answer is rw }

Then we can do the following:

role Answer { has $.answer is rw }
my $x = 100;
$x does Answer(42);
say $x; # 100
say $x.answer; # 42

If you're thinking now, "is this the return 0 but True thingy I once saw", the the answer is almost, yes. However, unlike "does", the "but" operator creates, operates on and returns a copy of the value rather than the original. It also does some special handling of values from enumerations. It is defined in terms of "does", so we're part of the way to "but". Getting the enumerations right is the harder part to implement.

The other major new feature I got in today was generic subs/methods. With these, you capture the type of a parameter in the call signature. You can then use it inside the sub or method. This means that we can write a sub that prints the type of the parameter it is passed just by doing:

sub saytype(::T $x) { say T }
saytype(42); # Int
saytype("Mam pivo"); # Str

You can use T anywhere you would use a type otherwise.

sub test(::T $x) { my T $y = "OH HAI" }
test(42) # Type check failed
test("hi") # Works

Note the variable $y inside the sub is constrained to be the same type as whatever the parameter to the sub was. In the first case, we pass an Int, so $y is constrained to be an Int and the assignment fails. In the second case, we pass a Str, so the assignment is successful. One remaining to-do feature is to let you use T elsewhere in the signature after you have declared it; that will be on my hit list for next week.

Finally, to round off the day, I took something a little easier. I've been eying enumerations for implementation, and the anonymous enum constructor was somewhat easier than the full-blown named one (which I'm still fully getting my head around). Basically, it can take a list of values and hand back a hash mapping each of them to ascending integers.

my %ooks = enum < ook! ook. ook? >;
say %ooks<ook!>; # 0
say %ooks<ook.>; # 1
say %ooks<ook?>; # 2

You can instead write a pair (in a list constructor that parses them as pairs, mind) to specify the starting value, which may be a string too.

my %meta = enum [ :foo('A'), 'bar', 'baz' ];
say %meta<foo>; # A
say %meta<bar>; # B
say %meta<baz>; # C

You can stick a pair at any point in there to change the indexing scheme too, or just use a load of mappings to set up your enum. At this point, it may seem like a fancy way of constructing a hash - and in a sense, it kinda is. However, the non-anonymous case introduces something somewhat more powerful, which as mentioned earlier can be used with "but", and also as a predicate function and in a smart match. Hopefully I get those in sometime in the coming weeks.

So, that's what got done on this week's Rakudo day. Another one next week, and thanks as always go to Vienna.pm for making this possible.

After not sleeping great, it took a couple of cups of tea to get me going this morning. I like coffee too, but haven't gotten around to getting myself a good coffee machine for this place yet, and nice tea beats instant coffee. (And this is how you know that, even though I live in Slovakia, my inner Britt is still alive.) Anyway, while I was getting properly conscious, and before digging into the main task of the day, I did a little more work on ranges: essentially, getting the versions of the range operator with endpoints to work. Thus you can now do:

say $_ for 1..5;    # 1\n2\n3\n4\n5\n
say $_ for 1^..5;   # 2\n3\n4\n5\n
say $_ for 1..^5;   # 1\n2\n3\n4\n
say $_ for 1^..^5;  # 2\n3\n4\n

So, that's another little bit of progress on ranges. Now, onto the meaty stuff. Today was, as the title hints, about attributes. For a while, you've been able to declare attributes on classes in Rakudo, but the support has been fairly primitive. Today I expanded it someone, and then I dug into getting composition of attributes from roles working. Composition of methods has worked for a while, but until today there was no support for attribute composition. So, some details.

First, accessor/mutator methods are now generated correctly. If you now write:

has $.badgers;

Then you get an accessor method. Trying to use it as an lvalue will now fail, as per the specification. If you want to have a mutator as well as an accessor, then you must write:

has $.badgers is rw;

Additionally, while you could put type constraints on variables and sub/method parameters before now, you couldn't put them on attributes. As of today, you can do so.

has Int $.badgers is rw;

And, as with variables, if you don't supply an initialization value, then in the case above your badgers attribute will be initialized to an Int proto-object. If you specify a role or some more complex or junctional type, then you just get a Failure object. Talking of junctional types, you can now list many types on variables (and attributes) and get them all enforced.

subset Percent of Int where 1..100;
subset Even of Int where { $^n % 2 == 0 };
my Even Percent $x = 42;   # OK
my Even Percent $y = 101;  # Type check failed
my Even Percent $z = 11;   # Type check failed

You can write disjunctions as well, according to The Spec, but I've not figured out how STD.pm proposes we parse those yet.

Then came getting attributes in roles working. In roles there are two ways to introduce attributes: with "has" and with "my". If you introduce a private attribute (the only kind you can introduce) with "my", then it is role private. It will not be accessible outside of the role (including in the class it is composed into or in other roles), will always get a slot of its own and will never conflict with attributes of the same name either in the class or from other roles. It is, as "my" suggests, lexically scoped within that role and invisible outside of it.

The other thing you can do is introduce attributes that get composed into the class, with "has". In this case, the semantics of the attribute are the same as if you had declared it in the class itself. However, you may also get a composition conflict. This happens when both the class and a role, or many roles, introduce attributes with the same name but different types. Basically, if there are multiple declarations of an attribute of the same name and they all have the same type, then they will all share one storage location. If they differ in type, it's a conflict (at composition time). This sounds odd at first, but actually makes properties work nicely.

All of what I just described is implemented as of today, and I've written basic tests for some of it (and more are to come, but I'm too tired to do any more today). It's a lot of detail, but I will try to explain it with some more concrete examples over the coming weeks as I get more progress in on roles. (I'll also talk about it in my Perl 6 OO talk at YAPC::EU, if you're going to be there; the slides will be available for everyone after the conference too.) Once again, a big thanks to Vienna.pm for funding this work.

After taking last week off for workshops, this week I got back to having my weekly Rakudo day. There were various things that were just about ready, and I've spent much of today getting those in place. The result is some new features, some fixes and some work on getting the semantics correct in places that we didn't before.

One of the big changes is that the range operator, "..", will now create a Range object, which is a lazy iterator. I'd done some of the initial work in writing the Range class before now, but using it blocked on some other list refactoring that was needed. That has now been done (thanks to Patrick for this), and today I was able to switch ".." over to creating the Range object rather than just a flat list. It still will be eager in a lot of places, because we haven't got a full lazy lists implementation just yet. However:

for 1..1000000 -> $x {
    say $x;
}

Will now not create an array of a million elements. In fact, it will run in constant space. I also implemented smart-matching against Range objects. This means that you can do things like:

if $x ~~ 1..100 { say "valid percentage" }

Again, this will not create a list 100 elements long, but instead just one Range object that knows its endpoints. There are still various things to do with ranges, including the operators for constructing ranges that are exclusive of their endpoints (and Range objects need some work for this themselves too). Also, we need to implement the :by adverb, but we can't parse adverbs on operators just yet. Oh, and then there's the fun of infinite ranges, but I'm hoping they will just fall out naturally with little to no changes to Range once the Inf type is in place.

I've been mumbling for a while about a bunch of work under the title of mutables. It has very much been an under-the-hood thing that was needed to make other things possible. Today I got us fully switched over to this model for scalars with no additional tests failing. And I also started taking of advantage of it to get a few more things in place.

Before, parameters were passed and were modifiable. However, unless you used "is rw" this should not have been so; they are meant to be readonly by default. Now we have the correct semantics, and "is rw", "is copy" and "is readonly" work too (but writing "is readonly" is a waste of time here, since it's the default anyway; I haven't bothered in the example below).

sub foo($x) { $x = 42; say $x; }
my $a = 100; foo($a); say $a; # Cannot assign to readonly variable
sub foo($x is rw) { $x = 42; say $x; }
my $a = 100; foo($a); say $a; # 42\n42\n
sub foo($x is copy) { $x = 42; say $x; }
my $a = 100; foo($a); say $a; # 42\n100\n

I also got the VAR($x) and $x.VAR macros in place (they are actually compiler macros, not sub or method calls), which let you get at the underlying implementation type - in this case the default Scalar object. In the end, this will let you work with traits and tied containers, though we are some way off being able to implement those yet (quite a few dependencies). For now you can do things like $x.VAR.readonly to find out if a variable is readonly, for example.

I've also started doing some preparations for the Next Big Thing I intend to do some work on during my Rakudo day next week: roles. We already have some basic composition support. I fixed using roles as type constraints on a variable. However, what we have so far isn't even scratching the surface of what we need to fully do roles. Attributes surely aren't done right, for example. I've spent some time reading the appropriate bits of S12, trying to get into my head what it means. I also got to discuss some bits of it with Larry (especially the type parameterization bits), and it seems I'm along the right lines. So, some work coming on roles next week, all being well.

I also fixed a range of smaller bugs.

  • The prefix + and - operators were not preserving integer type when performed on an integer.
  • The truncate method was not returning an Int, as its signature said it should.
  • Parrot's String Array PMCs were missing the get_iter vtable method to get an iterator for them; applied a patch from chromatic to add them and wrote a test.
  • Made subset ... of ... where syntax without a block work (you can just mention something there that is an expression to smartmatch against, which can be a load neater for some use cases)
  • Worked out what was wrong with my previous patch written over the weekend for a Parrot GC bug, and applied a corrected one, resulting in one less spectest failure and improved stability in interactive mode. Plus this helps everyone else using Parrot.
  • "my Foo $x" already correctly put a Foo protoobject in $x if Foo was a class, but did the wrong thing for roles and constraints. Now it does the right thing - sticks a Failure object in there.

So, a pretty productive day, if a bit all over the place. Thanks to Vienna.pm for funding this work, and for introducing me to a nice place to eat curry in Vienna after their Monday tech meet, which I popped along to. :-)

So, after some time "on the road", mostly spent going to Perl events, I'm finally back home, where I'll be pretty much staying for the next two and a half months. Certainly, I don't plan to fly anywhere before YAPC::EU::2008; no doubt I'll enjoy a couple of weekend breaks that I can take by train first, though. Home is currently Bratislava (capital of Slovakia), so having so many interesting bits of central and east europe so close to hand will be far too much to resist. I plan to make it over to the western Ukraine (I loved Kiev and want to check out Lvov), and hopefully see Poprad and take a bit of hiking in the nice mountains in that area.

I've now got all of my slides uploaded to my talks page, and here are a few direct links with summaries to help you get to the juicy stuff faster.

  • Implementing Perl 6, in Perl 6, on Parrot - I gave this one at the Nordic Perl Workshop, French Perl Workshop and to Vienna.pm. The slides were very nearly the same for all of them, so I just linked to the Vienna.pm version, which was the final one.
  • Perl 6 Tutorial - from the French Perl Workshop. This was delivered in the space of two hours, and assumes no prior knowledge of Perl 6 (but some basic Perl 5 knowledge). The vast majority of what it covers is usable today in Rakudo.
  • Understanding Perl 6 - this one could also have been called Perl 6 For Computer Scientists (there were variuous people who were doing academic research in computer science at the workshop). It's aimed at explaining how the features of Perl 6 fit together and giving a big picture of the language rather than dealing with syntax and practical usage.
  • Using Perl 6 And Parrot In Teaching - a look at how Parrot, PCT and Perl 6 could be used in teaching.
  • All Your Dynamic Languages Are Belong To Us - this was a guest lecture at a university in Stockholm to a bunch of undergraduate students. I introduced both Parrot and PCT, and then built a compiler live on stage. Overall, the talk lasted an hour; the compiler was built in just over 35 minutes.

Thanks to Best Practical and The Perl Foundation for giving me a grant to travel to help fund my travel to these events, to Claes Jakobsson for very kindly letting me stay at his place in Stockholm (and also for cooking some really delicious Swedish meatballs) and to the many people I met along the way who made the last couple of weeks a lot of fun.

About this Archive

This page is a archive of recent entries written by Jonathan Worthington in June 2008.

Jonathan Worthington: May 2008 is the previous archive.

Find recent content on the main index or look in the archives to find all content.

Subscribe

    Subscribe to rakudo.org

Powered by Movable Type 4.1
Technorati Profile