Posts Tagged ‘Rakudo Perl 6’

Day 2 – Rakudobrew

December 2, 2014

Traditionally in Perl, and probably in any other scripting language (I’m going to use the term “scripting” to describe “one that requires a runtime”, bear with me) there’s quite a big discrepancy between the latest released version of the toolchain and the one that your OS distribution ships (if it ships one at all!). My Ubuntu for instance, the freshly updated 14.10 not only offers me to install a nine-month-old Rakudo 2014.03, but also one running on top of Parrot, which I don’t remember voluntarily running anytime during 2014, frankly. Even if you look at languages with a longer release cycle, like Perl 5 which releases every year, system distributions shipping old versions is still a big deal: modern Perl modules still often maintain backwards compatibility with Perl 5.8 released in 2002, because that’s what some versions of CentOS comes with.

A popular idea is to say that system Perl (or system Python, system Ruby or whatever floats your boat) is for the system itself, while we programmers can freely uses whatever version we wish as long as we install it and maintain it in our home directory and not mess with system stuff. Perl 5 people usually recommend either Perlbrew or plenv for that; in Perl 6 rakudobrew is often said to be the easiest way to stay up-to-date with recent developments.

Rakudobrew is a commandline tool that makes it easy to install and update different flavours of Rakudo, as well as switch between them any time you want to. It also updates Panda, the module installer, after every update, along with all modules you had installed before (precompiled modules need to be updated after every update of Rakudo).

I made an effort to keep rakudobrew reasonably free of dependencies: it only requires Perl 5 and git, which are roughly what’s required to build Rakudo anyway. It’s known to work well on Linuxes and OSX, but I’ve also recently got some pull requests to improve Windows support (which apparently worked for a while by itself, without me knowing about it. Cool!) so it may happen to be useful to you, our Windows-running readers just as well.

Rakudobrew requires almost zero setup to get running; the only thing you need to do is to get it from Github (either by cloning it or downloading a tarball from the website), and get your system to recognize its bin/ subdirectory as part of your PATH. What I’m usually going for on my machines is the following:

$ git clone https://github.com/tadzik/rakudobrew ~/.rakudobrew
$ export PATH=~/.rakudobrew/bin:$PATH
$ echo 'export PATH=~/.rakudobrew/bin:$PATH' >> ~/.zshrc
$ # or ~/.bashrc or whatever

After that it requires nothing like ‘rakudobrew init’ or anything; it’s ready to get useful. The most basic thing you need from it is of course installing Rakudo:

$ rakudobrew build moar

The above command will download and build Rakudo on MoarVM, the latest version available from Github. For extra stability you may want to install the latest available release and just stick to that:

$ rakudobrew build moar 2014.11

This will take a while, but after a while it should finish saying “Done, moar-2014.11 built.” From that point on you are ready to go; if you adjusted your PATH correctly, the “perl6″ command will be available for you to use, and you’re ready to hack!

That said, you may want something more from life than just the bare compiler. The “useful and usable” Perl 6 distribution called Rakudo Star doesn’t have its own Starbrew, but that doesn’t mean you can’t eat the cake and still have it. Let’s also install Panda, the module installer, and whatever Rakudo Star usually ships.

$ rakudobrew build panda
$ panda install Task::Star

This gets you not only the module installer (which is instantly ready to go, just as shown above), but also all the widely appreciated goodies like: NativeCall, Term::ANSIColor, DBIish or Grammar::Debugger. Life’s good!

But what if you want more from life than just MoarVM? Perhaps you want to try out the famous JVM interop? Couldn’t be easier!

$ rakudobrew build jvm
$ rakudobrew switch jvm

Contrary to what it may suggest the above commands don’t build the JVM itself (you’re expected to have that installed), but the version of Rakudo running on the Java Virtual Machine. Note that you’ll still have to install panda and all the modules you want; those are installed per every instance of Rakudo you built. In case you got lost in all the versions you installed, the list command is there to help you again:

$ rakudobrew list
  jvm-HEAD
  moar-2014.11
* moar-HEAD

The asterisk is marking the one currently active (‘rakudobrew current’ can also tell you that). You don’t need to type the full name either; both ‘rakudobrew switch jvm’ and ‘rakudobrew switch moar-H’ will work just fine.

In case you want to get rid of something you installed (perhaps you wanted to test your code on some older version of Rakudo, or see how slow things were back in the days (the further you reach the more amazing it gets)), it’s as easy as removing a subdirectory from rakudobrew’s directory:

$ rm -rf ~/.rakudobrew/jvm-HEAD

Or in case you want to get rid of the entire rakudobrew for any reason, you don’t have to be afraid of any globally installed leftovers: there aren’t any. It’s all self-contained, so as simple as

$ rm -rf ~/.rakudobrew

And poof! Gone. Before you get rid of it though, you may want to keep your still warm Rakudo around to try out more interesting stuff that our Advent Calendar will have to show you in the coming weeks. Stay tuned!

Day 20 – How to mangle the initial state

December 20, 2013

The past year saw the (at least partial) implementation of several variable traits. But before we get into that, the past year also saw Nil getting implemented closer to spec in Rakudo. If you read the spec, you will know that Nil indicates the absence of a value. This is different from being undefined, as we saw in an earlier blogpost this year. So what does that mean?

$ perl6 -e 'my $a = 42; say $a; $a = Nil; say $a'
42
(Any)

So, assigning Nil will reset a scalar container to its default value, which (by default) is the type object of that container. Which is (Any) if nothing is specifically specified.

Enter the “is default” variable trait

If you want to specify the default value of a variable, you can now use the “is default” variable trait:

$ perl6 -e 'my $a is default(42); say $a; say $a.defined’
42
True

So oddly enough, even though we haven’t assigned anything to the variable, it now has a defined value. So let’s assign something else to it, and then assign Nil:

$ perl6 -e 'my $a is default(42) = 69; say $a; $a = Nil; say $a'
69
42

Of course, this is a contrived example.

“is default” on arrays and hashes

It gets more interesting with arrays and hashes. Suppose you want a Bool array where you can only switch “off” elements by assigning False? That is now possible:

$ perl6 -e 'my Bool @b is default(True); say @b[1000]; @b[1000]=False; say @b[1000]'
True
False

Of course, type checks should be in place when specifying default values.

$ perl6 -e 'my Bool $a is default(42)'
===SORRY!=== Error while compiling -e
Type check failed in assignment to '$a'; expected 'Bool' but got 'Int’

Note that this type check is occurring at compile time. Ideally, this should also happen with arrays and hashes, but that doesn’t happen just yet. Patches welcome!

Using is default on arrays and hashes interacts as expected with :exists (well, at least for some definition of expected :-):

$ perl6 -e 'my @a is default(42); say @a[0]; say @a[0].defined; say @a[0]:exists'
42
True
False

Note that even though each element in the array appears to have a defined value, it does not necessarily exist. Personally, I would be in favour of expanding that to scalar values as well, but for now :exists does not work on scalar values, just on slices.

It’s not the same as specifying the type

What’s wrong with using type objects as default values? Well, for one it doesn’t set the type of the variable. Underneath it is still an (Any) in this case:

$ perl6 -e 'my $a is default(Int) = "foo"; say $a'
foo

So you don’t get any type checking, which is probably not what you want (otherwise you wouldn’t take all the trouble of specifying the default). So don’t do that! Compare this with:

$ perl6 -e 'my Int $a = "foo"'
Type check failed in assignment to '$a'; expected 'Int' but got 'Str'

Which properly tells you that you’re doing something wrong.

Nil assigns the default value

Coming back to Nil: we already saw that assigning it will assign the default value. If we actually specify a default value, this becomes more clear:

$ perl6 -e 'my $a is default(42) = 69; say $a; $a = Nil; say $a'
69
42

In the context of arrays and hashes, assigning Nil has a subtly different effect from using the :delete adverb. After assigning Nil, the element still exists:

$ perl6 -e 'my @a is default(42) = 69; say @a[0]; @a[0] = Nil; say @a[0]:exists'
69
True

Compare this with using the :delete adverb:

$ perl6 -e 'my @a is default(42) = 69; @a[0]:delete; say @a[0]; say @a[0]:exists'
42
False

One could argue that assigning Nil (which assigns the default value, but which is also specced as indicating an absence of value) should be the same as deleting. I would be in favour of that.

Argh, I want to pass on Nil

The result of a failed match, now also returns Nil (one of the other changes in the spec that were implemented in Rakudo in the past year). However, saving the result of a failed match in a variable, will assign the default value, losing its Nilness, Nilility, Nililism (so much opportunity for creative wordsmithing :-).

$ perl6 -e 'my $a = Nil; say $a'
(Any)

It turns out you can specify Nil as the default for a variable, and then It Just Works™

$ perl6 -e 'my $a is default(Nil) = 42; say $a; $a = Nil; say $a'
42
Nil

Introspection

You can use the VAR macro to introspect variables to find out its default value, without having to do something as destructive as assigning Nil to it:

$ perl6 -e 'my @a is default(42); say @a.VAR.default'
42

This also works on system variables like $/:

$ perl6 -e 'say $/.VAR.default'
Nil

And can also be used to interrogate other properties:

$ perl6 -e 'say $/.VAR.dynamic’
True

But this gets us into material for a blog post for another day!

Conclusion

The “is default” variable trait allows you to manipulate the default value of variables and elements in arrays and hashes. Together with the new meaning of Nil, it is a powerful means of mangling initial values of variables to your satisfaction.

Day 05 – Changes in specification and operational fallout

December 4, 2013

Perl 6 has become much more stable in the past year. There have however been some potentially disrupting changes to the Perl 6 specification, followed by implementation changes to adhere to that new spec.

bless() changes

One of the most visible changes is the removal of an object candidate in bless(). If you wanted to call bless() yourself in your code, rather than supplying your own BUILD() method, you needed to provide an object candidate as the first parameter. Over the years, this turned out to basically always be * (as in Whatever). Which is pretty useless and an obstacle for future optimisations. So TimToady invoked rule #2 to remove that first parameter.

The changes to calls to bless() in the core setting were implemented by moritz++. For those Perl 6 modules in the wild, a warning was added:

 Passing an object candidate to Mu.bless is deprecated

The first parameter would then be removed and execution would continue.

This has the disadvantage of generating a warning every time you create an object of that class with the deprecated call to bless(). So there must be a better way to do this!

Enter “is DEPRECATED”

It turns out there is a better way. Already in June 2012, pmichaud++ added an “is DEPRECATED” routine trait that did nothing until earlier this year when I decided to add some functionality to it. Initially it was just a warn, but that just had the same annoying quality as the warning with bless().

Since the idea behind the “is DEPRECATED” trait was not specced yet, I figured I could turn it any way I wanted, unless I would not be forgiven by the #perl6 crowd. So I re-used an idea I had had at former $work, already years ago. Instead of warning at the moment a transgression is spotted, it feels better, especially for these types of deprecations, to just remember where these transgressions take place. Only when the program is finished, report the transgressions that were spotted (on STDERR).

One of the other standard methods that has been deprecated in Perl 6, is ucfirst(). One should use the tc() (for “title case”) method instead. So what happens if you do call ucfirst()? That is easily demonstrated with a one-liner:

$ perl6 -e 'say "foo".ucfirst; say "done"'
Foo
done
Saw 1 call to deprecated code during execution.
================================================================================
Method ucfirst (from Cool) called at:
  -e, line 1
Please use 'tc' instead.
--------------------------------------------------------------------------------
Please contact the author to have these calls to deprecated code adapted,
so that this message will disappear!

After this has been live in the repo for a while, and spectested, and since nobody on #perl6 complained, I decided to spec this behavior not only for routines, but also for attributes and classes. Unfortunately, the latter ones have not been implemented yet (although you can already specify the traits). But there is a patch -p1 coming up, which should give me some quality time to look at this.

So why is bless(*) not properly DEPRECATED

Indeed. Why? Simply because I missed it. So I just fixed this: nothing like blog-driven development! So this one-liner now says:

$ perl6-p -e 'class A { method new { self.bless(*) } }; say A.new'
A.new()
Saw 1 call to deprecated code during execution.
================================================================================
Method bless (from Mu) called at:
  -e, line 1
Please use a call to bless without initial * parameter instead.
--------------------------------------------------------------------------------
Please contact the author to have these calls to deprecated code adapted,
so that this message will disappear!

So how do you specify the text?

The “is DEPRECATED” trait currently takes one parameter: the string to be shown between Please use and instead. If you don’t specify anything, the text something else will be assumed. Since that is not entirely useful, it is advised to always specify a text that makes sense in that context. Additional parameters may be added in the future to allow for more customisation, but so far they have not been needed.

Conclusion

Perl 6 will continue to evolve. Changes will still be made. To not break early adopter’s code, any non-compatible changes in the implementation of Perl 6 can be marked as deprecated without interfering with the execution of existing programs much. Perl 6 module authors can do the same should they feel the need to change the API of their modules.


Follow

Get every new post delivered to your Inbox.

Join 43 other followers