Jonathan Worthington: May 2008 Archives
The last few days have been pretty action-packed, so I'm going to try and summarize what's been happening in this post.
First up, Rakudo. So far I've been writing a lot of "hey, look, now you can do X!!!" style posts. However, developing a large piece of software (such as the Perl 6 compiler) isn't just about the fun parts: it's also about the things that Just Need Doing too. I spent much of my Rakudo day, on Wednesday last week, working with Patrick on one of these things. Before, we had multiple proto-object models for PGE and Rakudo. Grammars were somewhat special-cased. Also, the meta-class stuff wasn't quite right. Patrick did a chunk of work to build a unified model that we could use for all of these, which was more correct that what we had before. Then we worked together to get Rakudo switched over to the new model. The effects of this won't be immediately visible, but it certainly makes doing another bunch of stuff easier (I think class methods are possible to implement quite cleanly now, for example; it'll also be much easier to get class intropsection support in).
On Thursday afternoon I took a small propellor plane from Bratislava to Prague, and then flew from Prague on to Stockholm for the Nordic Perl Workshop. Klaes Jakobsson, one of the conference organizers and a wonderful host (he lives in a nice part of Stockholm and cooks wonderful Swedish meatballs) kindly allowed me to stay at his place from Thursday through to Monday. On the Friday I gave a lecture at the university at Kista (pronounced Shista...it's almost like they want to compete with English spelling "rules" or something) to a bunch of Computer Science students, telling them about Parrot and the Parrot Compiler Toolkit. I built a mini-language (Turing complete and all) right there in the lecture, which was well received. I think people understood it at different levels, but after I had given the talk some people who spoke to me seemed really excited to play with this themselves. Others I think went away with a good high-level picture that we're building a VM to allow many dynamic languages to inter-operate.
The Nordic Perl Workshop took place on Saturday and Sunday. I greatly enjoyed the conference, and for me it was the best set of talks that I have seen for quite a while. That's in part because they were in English, so I could understand them all, but even more because of the topics. The organizers had invited a couple of people working on stuff other than Perl along to the conference too. There was a talk about the Smalltalk web framework called Seaside, which was highly interesting and thought-inspiring. There was also a talk from some folks working on a dynamic research language, which gave a nice glance into another world. I think they got something from being at the Perl conference too. It makes me really happy to see interaction with other communities, and I hope to see more of this kind of thing, so we can share something of Perl with others, and can listen with genuine interest to see what others are doing too.
I gave two talks, one on the Perl 6 language and one looking more at Rakudo. They were both enjoyable to give, not only because they relate to things I am working on but also because of the audience, who asked some great and at times quite challenging questions. I hope that I was able to give everyone good answers. I will get the slides online and linked to very shortly, both for the NPW and from the Kista.
I did less coding at this workshop because I wanted to pay attention to the talks and have interesting discussions with people, but I did get some in. Again, it's refactoring, but it's work that will enable a lot more stuff to happen. For one, it will make assignment and type checking vastly cheaper. It will also make it easier to get things like "is rw" and "is ro" in place too - expect those semantics to be correct in the not too distant future. It's also laying some very early foundations for lazy lists, which will be needed to get ranges (which are lazy iterators) working properly. It's a little way down the roadmap, but I'm hoping to get a basic implementation together in the not too distant future.
I'm not having a Rakudo day this week, but I am be attending the French Perl Workshop, where I am giving more talks and, given my (very low) French level, be getting some coding time in too.
I have a feeling that my talks and discussions at the NPW have attracted some additional interest in both the Perl 6 and Parrot projects here at the Nordic Perl Workshop, which I'm hopeful might translate into more people joining in the fun of implementing and testing the two of them, or building other language compilers for Parrot. It's exciting times for both projects, and I'm really happy to be a part of it all. Of course, life and flights cost money, so I'd like to thank both Vienna.pm for funding the day of work on Rakudo last week, and also the Perl Foundation and Best Practical for helping to fund my travel to the Nordic and French Perl Workshops.
After yesterday's day of Rakudo hacking, I was too tired to write up what had happened coherently, so I left it until today. I did quite a few different things throughout the day, so rather than taking them in order I'll group them in a way that makes it a bit easier to follow.
First, I dealt with some things from the perl6-compilers list. There was a patch from Moritz to factor out a large chunk of one of the methods in the parser actions file to help make it more readable, which I applied. There was also a bug report from Chris Fields, pointing out that typed variables had stopped working. After trying to work out why from some debugging, I then started working out which check-in had caused the problem, which soon led to a fix. So, type annotations will now work again.
I had tried to get some basic capture support in place before, but it was based upon the Capture PMC. For some reason, this didn't work out so well, and I was keen to have captures for something else I was going to implement later on. Therefore, I did a quick PIR version. I also implemented the $( ), @( ) and %( ) contextualizers (which put whatever is inside the brackets into item, list and hash context respectively). Therefore, you can now do things like:
my $x = \(1, 2, 3, foo => 42);
say $x[1]; # 2
say $x<foo>; # 42
for @($x) -> $val {
say $val; # 1\n2\n3\n
}
There's plenty more to do on captures, but you have the basics now. And of course, the contextualizers work much more generally than just with captures, but we've some work to do in order to make these useful in the general case.
Each week I'm implementing a little more of S12 (the objects specification). This week was no exception. We now have basic support in for declaring and calling private methods.
class A {
my method private {
12;
}
method public {
self!private
}
}
say A.new().public; # 12
Notice how they are called with an entirely different syntax, so its obvious when looking at the code that you're calling something private. Talking of different calling syntaxes, there are some other variations to just writing "." to call a method. .? will call a method of that given name if there is one, or just return an undef (rather than throwing an exception) if there is no such method. .* will call all methods in the class hierarchy with that name, including inherited ones, and return a list of captures containing the values that were returned, and if there are no methods you will just get an empty list. .+ is like .*, but if it can't find at least one method to call it throws an exception. Think of them like quantifiers on regexes, but relating to method calls instead.
class Dog {
method bark { say "WOOF" }
}
class Puppy is Dog {
method bark { say "woof" }
}
my Puppy $x .= new();
$x.?bark(); # woof\n
$x.?doesnotexist(); # no exception
$x.+bark(); # woof\nWOOF\n
I haven't collected return values into captures in these examples, but it works too.
Something else that had been on the want list for a while was allowing explicit specification on a variable to hold the invocant of a method. This turned out to be trivial to implement, so rather than just having the self keyword you can now say:
class Foo {
method test($inv: $x, $y) {
say $inv.WHAT();
say $x + $y;
}
}
my $x = Foo.new();
$x.test(14, 28); # Foo\n42\n
As a slightly ad-hoc thing, I implemented the prefix ^ operator. On the name of a class, this gets the meta-class. On a number, it generates a range from 0 up to that value minus one. So the following prints the numbers 0 through 4, each on a line of their own.
for ^5 -> $n {
say $n
}
This was actually just a trivial task I spotted while persuing the specification for ranges. We just fake them up in Rakudo at the moment to produce a list, but they are supposed to be lazy and have a whole load of other semantics. I stubbed in a Range class to get us started in that direction, but it needs quite a bit more work before we're ready to actually get Rakudo's ".." operator to construct it. For now it just stores its start and end points and knows how to give a Perl representation of itself.
Once again, thanks for Vienna.pm for sponsoring this work. Also, thanks to everyone else contributing to Rakudo, be it with patches, bug reports or general feedback. :-)
I started out the day by looking through the RT queue for Rakudo. Two tickets were already dealt with, so I just closed those. Another was a bug report concerning assigning undef to typed variables. Doing:
my Int $x = undef;
Would give a type check failure. This is now resolved. Furthermore, if your type is a class name, then assigning undef at any point to it will result in it holding the protoobject for that class again. I also took a moment to post to Perl 6 Language to get some clarifications on what "my TypeName $x;" left $x being when $x was a role, subset type or a junction of types.
Last week I started getting grammars in place. I got so far as having the regex live in the correct namespaces, but that didn't make grammars at all class-like, which is how they should be. This week I set out to fix that. Grammars now get protoobjects too, which you can call .WHAT and so forth on. Furthermore, I got inheritance working and also smart-matching against a grammar, which runs it's TOP rule. Therefore you can now run the following example in Rakudo.
grammar Loads { regex Lots { \d+s } };
grammar Many is Loads { rule TOP { of } };
if "100s of 1000s" ~~ Many {
say $/; # 100s of 1000s
}
Having closed four RT tickets so far, I took a look through there to see what else there was. There was one that did most of what was needed to implement the .perl method on Junctions (which you can call, in theory anyway, on anything to get a Perl representation of it). I did the required fixes and applied it, but realized in the process that we were missing .perl for any of the really fundamental types, so I added it for Num, Int and Str.
With that done, I spent a little time on the S12 tests. I added fudge directives to get one of the that failed to parse to do so, and added an extra test. I plan to add much more here and flesh out the tests quite a bit over time.
Turning back to the OO support, I did some updates to the grammar that both brought us closer to STD.pm - the official grammar - and added the ability to parse a range of extra things. The first grammar changes were related to method calling. Normally you call a method just with ".", but private methods are called with "!". Additionally, there are ways to call sets of methods with quantifiers (.?, .+ and .*, with meanings analogous to those in regexes). Finally, I added the ability to scope declarations of routines too, so we can now parse lexical subs and private methods. I stubbed in conditionals for all of these cases that throw unimplemented exceptions, so people didn't use them expecting that because they parse, they will also work.
So, now there's a bunch of stubs in there for another bunch of OO features and, if nobody beats me to it, I'll be filling some of those out on my next Rakudo day, or maybe before then if time allows (though I'm moving apartment - and country - over the coming week, so I'm not expecting to have much time). A big thanks to Vienna.pm for funding today's work.
Today's work has been a mixture of refactoring and clean-ups that had been on the want list for a while, but just hadn't happened, as well as making some new things work.
First, the initial work I did on types attached them to variables, but what we really needed was a more general way to attach properties. Therefore, there is now a hash of properties instead, where we can stash other stuff.
Next up, I had based pairs on the Parrot Pair PMC, though as Patrick pointed out it's so far off being right for Perl 6 (for example, it's mutable, the Perl 6 one isn't) that we might as well just have our own. Dropping the Parrot Pair PMC and doing that took me all of ten minutes of work, and we get the semantics of pairs a bit more correct too. So that's much cleaner now.
A few days back, dakkar sent in a bug report regarding inheritance. It was almost correct code, but didn't work on Rakudo, since initialization of parent attributes was not yet implemented. I've now implemented this, and I'll borrow the example from the bug report to demonstrate it.
class Foo {
has $.x;
method boo { say $.x }
}
class Bar is Foo {
method set($v) { $.x = $v }
}
my Foo $u .= new(:x(5));
$u.boo; # 5
$u= Bar.new(Foo{ :x(12) }); # This is what now works
$u.boo; # 12
$u.set(9);
$u.boo; # 9
This is not some magical hacky syntax just to make constructors work; you can use it in the general case to associate some vivification data with a proto-object, which gives you a copy of it back with the data attached. It's a bit like currying the object instantiation. So after making the above work, it wasn't much more work to get the following working.
class Foo { has $.x }
my $foo42 = Foo{ :x(42) };
my $test = $foo42.new();
say $test.x; # 42
Note that the original Foo itself isn't changed. We'll have to revisit this again later, because the way I've done it now doesn't have the lazy semantics it's eventually meant to have. It makes the common use case work, though.
With some time spent on objects, I moved onto some improvements to regex stuff. The upshot of this is that you can now use grammar to group regexes into a namespace.
grammar Test {
regex Load { \d+s };
rule Loads { <Load> of <Load> };
}
if "100s of 1000s" ~~ Test::Loads { say "yes" }
yes
Note that this is just the start of grammars; inheritance doesn't yet work and you can't smart-match against them yet. It's a stop forward, though.
If you're an avid Rakudo follower, you'll have noted that regex, rule and token all (wrongly) did the same thing before today. I've fixed that too now (there was some behind the scenes work in being able to pass options to the compiler that will be useful elsewhere, as to users of the Parrot Compiler Toolkit in general). In a nutshell, token and rule don't backtrack, where as regex does, and additionally rule translates spaces to the
# Demonstrating :ratchet semantics (rule like token here).
regex WillBT { a*a }
token WontBT { a*a }
if "aaa" ~~ WillBT { say "yes" } else { say "no" }
yes
if "aaa" ~~ WontBT { say "yes" } else { say "no" }
no
# Demonstrating :sigspace semantics.
regex Test1 { \d \d };
rule Test2 { \d \d };
if "12" ~~ Test1 { say "yes" } else { say "no" }
yes
if "1 2" ~~ Test1 { say "yes" } else { say "no" }
no
if "12" ~~ Test2 { say "yes" } else { say "no" }
no
if "1 2" ~~ Test2 { say "yes" } else { say "no" }
yes
So, that's what got done today. I'd like to thank Vienna.pm for funding this work, and hope you'll have fun playing with it, breaking it and reporting bugs. :-)
First of all, before I dig into what my recent Rakudo hackings have been, I'd like to thank Vienna.pm for funding me to work on Rakudo. I will be working one full day a week on Rakudo from now on, at least for the next three months and, hopefully, longer. Today is the first day I'm working under this funding, so I'll be posting again later on today about what I got done. This post is just to update you on little bits that I've been doing, but didn't get written up yet.
First of all, you can now use the .= operator.
class Foo { }
my Foo $x .= new();
Here we call the 'new' method on $x, which we know is of type Foo thanks to the type declaration, and assign what it returns - namely, an instance of Foo - to $x. I did initially put this in a while ago, but it was a tad buggy and I wanted to get those worked out before posting it. That's been done, so happy playing. (And note you can use it in places other than declarations too.)
Additionally, some very basic multi-method dispatch based upon types is now in place. You can only use class names, not constraints or role names at the moment for the types, and certainly not more complex types than that. However, it's a start and allows us to run the following example.
class Thing {}
class Rock is Thing {}
class Paper is Thing {}
class Scissors is Thing {}
multi sub defeats(Thing $t1, Thing $t2) { 0 };
multi sub defeats(Paper $t1, Rock $t2) { 1 };
multi sub defeats(Rock $t1, Scissors $t2) { 1 };
multi sub defeats(Scissors $t1, Paper $t2) { 1 };
my $paper = Paper.new;
my $rock = Rock.new;
say defeats($paper, $rock); # 1
say defeats($rock, $paper); # 0
Finally, I put in a small optimization to avoid having to run some runtime type-checks when we can statically determine they're not needed. This should help performance a little.
