Day 12 – I just felt a disturbance in the switch

So I said “I’m going to advent post about the spec change to given/when earlier this year, OK?”

And more than one person said “What spec change?”

And I said, “Exactly.”


We speak quite proudly about “whirlpool development” in Perl 6-land. Many forces push on a particular feature and force it to converge to an ideal point: specification, implementation, bugs, corner cases, actual real-world usage…

Perl 6 is not alone in this. By my count, the people behind the HTML specification have now realized at least twice that if you try to lead by specifying, you will find to your dismay that users do something different than you expected them to, and that in the resulting competition between reality and specification, reality wins out by virtue of being real.

I guess my point is: if you have a specification and a user community, often the right thing is to specify stuff that eventually ends up in implementations that the user community benefits from. But information can also come flowing back from actual real-world usage, and in some of those cases, it’s good to go adapting the spec.


Alright. What has happened to given/when since (say) some clown described them in an advent post six years ago?

To answer that question, first let me say that everything in that post is still true, and still works. (And not because I went back and changed it. I promise!)

There are two small changes, and they are both of the kind that enable new behavior, not restrict existing behavior.

First small change: we already knew (see the old post) that the switching behavior of when works not just in a given block, but in any “topicalizer” block such as a for loop or subroutine that takes $_ as a parameter.

given $answer {
    when "Atlantis" { say "that is CORRECT" }
    default { say "BZZZZZZZZZT!" }
}
for 1..100 {
    when * %% 15 { say "Fizzbuzz" }
    when * %% 3 { say "Fizz" }
    when * %% 5 { say "Buzz" }
    default { say $_ }
}
sub expand($_) {
    when "R" { say "Red Mars" }
    when "G" { say "Green Mars" }
    when "B" { say "Blue Mars" }
    default { die "Unknown contraction '$_'" }
}

But even subroutines that don’t take $_ as a parameter get their own lexical $_ to modify. So the rule is actually less about special topicalizer blocks and more about “is there something nice in $_ right now that I might want to switch on?”. We can even set $_ ourselves if we want.

sub seek-the-answer() {
    $_ = (^100).pick;
    when 42 { say "The answer!" }
    default { say "A number" }
}

In other words, we already knew that when (and default) were pretty separate from given. But this shows that the rift is bigger than previously thought. The switch statement logic is all in the when statements. In this light, given is just a handy topicalizer block for when we temporarily want to set $_ to something.

Second small change: you can nest when statements!

I didn’t see that one coming. I’m pretty sure I’ve never used it in the wild. But apparently people have! And yes, I can see it being very useful sometimes.

when * > 2 {
    when 4 { say 'four!' }
    default { say 'huge' }
}
default {
    say 'little'
}

You might remember that a when block has an implicit succeed statement at the end which makes the surrounding topicalizer block exit. (Meaning you don’t have to remember to break out of the switch manually, because it’s done for you by default.) If you want to override the succeed statement and continue past the when block, then you write an explicit proceed at the end of the when block. Fall-through, as it were, is opt-in.

given $verse-number {
    when * >= 12 { say "Twelve drummers drumming"; proceed }
    when * >= 11 { say "Eleven pipers piping"; proceed }
    # ...
    when * >= 5 { say "FIIIIIIVE GOLDEN RINGS!"; proceed }
    when * >= 4 { say "Four calling birds"; proceed }
    when * >= 3 { say "Three French hens"; proceed }
    when * >= 2 {
        say "Two turtle doves";
        say "and a partridge in a pear tree";
    }
    say "a partridge in a pear tree";
}

All that is still true, save for the word “topicalizer”, since we now realize that when blocks show up basically anywhere. The new rule is this: when makes the surrounding block exit. If you’re in a nested-when-type situation, “the surrounding block” is taken to be the innermost block that isn’t a when block. (So, usually a given block or similar.)


It’s nice to see spec changes happening due to how things are being used in practice. This particular spec change came about because jnthn tried to implement a patch in Rakudo to detect topicalizer blocks more strictly, and he came across userland cases such as the above two, and decided (correctly) to align the spec with those cases instead of the other way around.

The given/when spec has been very stable, and so it’s rare to see a change happen in that part of the synopses. I find the change to be an improvement, and even something of a simplification. I’m glad Perl 6 is the type of language that adapts the spec to the users instead of vice versa.

Just felt like sharing this. Enjoy your Advent.

Day 11 – The Source will be with you, always

Reportings from a Learnathon

This past weekend I had the pleasure of hosting a Perl 6 learnathon with a friend who contacted me specifically to have a night of absorbing this new version of Perl. I thought it might be interesting to share some of what we learned during the process. I will begin with by explaining the single line of code which ended up and then show you some examples of where our evening took us.

Pick Whatever, rotor by five

As we opened our laptops, I was excited to show the newest example I had pushed to my Terminal::Print project. It’s taken quite some time to achieve, but asynchronous printing is now working with this module. It’s not fast, yet, but my long saught multi-threaded “Matrix-ish” example is working. Each column is being printed from an individual thread. This approach of spinning off a bunch of new threads and then throwing them away is not efficient, but as a proof of concept I find it very exciting.

pick-rotor

This line contains a few things that inspired questions. The first is the precise meaning of pick(*), which here means that we want to produce a randomized version of @columns. You can think of the Whatever here as a meaning “as many as possible”. It triggers a different multi method code path which knows to use the invoking objects’s own array size as the length of the list to pick.

The second part to explain was rotor. This is another one of those Perl 6 English-isms that at first felt quite strange but quickly became a favorite as I began visualizing a huge turbine rotor-ing all of my lists into whatever shape I desire whenever I use it. In this case, I want to assemble a list of 5-element arrays from a random version of @columns. By default, rotor will only give you back fully formed 5-element arrays, but by passing the :partial we trigger the multi-method form that will include a non 5-element array if the length of @columns is not divisible by 5 (‘Divisibility’ is easily queried Perl 6, by the way. We phrase it as $n %% 5.)

Put another way: rotor is a list-of-lists generator, taking one list and chopping it up into chunks according to your specifications. My friend mentioned that this resembles a question that he asks in interviews, inviting an investigation into the underlying implementation.

I’ve always considered Rakudo’s Perl 6-implemented-in-Perl 6 approach as a secret weapon that often goes overlooked in assessment of Perl 6’s viability. Even with the current reliance on NQP, there is a great deal of Perl 6 which is implemented in the language itself. To this end, I could immediately open src/core/Any.pm in an editor and continue explaining the language by reading the implementation of the language. Not just explaining the implementation, which can be accomplished by the right combination of language/implementation language and polyglot coverage. I mean explaining the language by looking at how it is used to implement itself, by following different threads from questions that arise as a result of looking at that code.

A word of caution

Now, I don’t mean to imply that one’s initial exposure to core can’t be a shocking experience. You are looking at Perl 6 code that is written according to constraints that don’t exist in perl6 which arise from not being fully bootstrapped and performance concerns. These are expressed in core by NQP and relative placement in the compilation process, on the one hand, and in prototyping and hoop jumping, on the other.

In other words: you are not expected to write code like this and core code does not represent an accurate representation of what Perl 6 code looks like in practice. It is 100% really Perl 6 code, though,  and if you look at NQP stuff as funky library calls, everything you are seeing is possible in your own code. You just don’t normally need to.

Down the rabbit hole

From src/core/Any.pm, here is the code for rotor:

any-rotor.png

And the code for pick:

pick-any

These are not the actual implementations, mind you. Those live in List.pm. But already these signatures inspire some questions

What is this |c thing in the signature?

This represents a Capture, which is an object representing the arguments passed into the method call. In this case it is being used such that no unpacking/containerization occurs. Rather we simply accept what we have been given and pass them as a single parameter to self.list.rotor without looking at them.

In the proto signature for pick we see that there is no name for the Capture, but rather a bare ‘|‘ which tells the method dispatcher that there can be zero or more arguments. Put another way: there are no mandatory arguments that apply to all the pick candidates.

What is this proto thing?

The answer is that it is a clarification that you can apply to your multi methods that constrains them to a specific “shape”.  It is commonly used in conjunction with a Capture, and in fact we see this in our pick example.

As the docs put it, a proto is ‘a way to formally declare commonalities between multi candidates’. The prototype for pick specifies that there are no mandatory arguments, but there might be. This is basically the default behavior for multi method dispatch, but here it allows us to specify the is nodal trait, which provides further information to the dispatcher relating to the list-y. Also due to bootstrapping issues, all multi methods built into Rakudo need an explicit proto.In practice we do not need either of these for our code. But they are there when you need them. One example of a trait that you might use regularly is the pure trait: when a given input has a guaranteed output, you can specify that it is pure and the dispatcher can return results for cached arguments without repeated calculations.

Midnight Golf

As promised, here are a few code examples from the learnathon.

stateful.pngstateful-results.png

This is using anonymous state variables, which are available to subroutines and methods but not to other Callables like blocks.  My friend shared my marvel at the power and flexibility of the Perl 6 parser to be able to handle statements like $--*5, when every character besides the 5 in that statement has a different meaning according to context. Meanwhile Perl 6 gives you defaults for your subroutine parameters by using the assignment operator.

Note that each bare $ in a subroutine creates/addresses a new, individual state variable. Some people will hate this, as they hate much that I appreciate about the language. These anonymous state variables are for situations where a name doesn’t matter, such as golfing on the command line. They can be confusing to get a full grasp of, though.

Here is another example we generated while exploring (and failing many times) to grasp the nuances of these dynamics.

more-state.pngmore-state-results.png

Gone is the anonymous state variable. This is by necessity, because you can only refer to an anonymous state variable once in a subroutine. We’ve switched $a to be optional. The parens around the state declaration are necessary because trying to declare a variable in terms of itself is undefined behavior and Perl 6 knows that.

The same thing, expressed in slight variations but with the same meaning:

more-more-state.png

The bottom example shows that the assignment to zero in our initial more-state is actually unnecessary. The middle shows creating a variable and binding it to a state var, which is a rougher way to get the same behavior as openly declaring a state variable. The top example shows what might be considered the ‘politely verbose’ option.

Concluding thoughts

I was hoping to share more from the evening with you, but it’s been a lot of words already and we’ve only scratched the surface of the first example we examined! Instead, I recommend that you spend some time with src/core in an editor,  the perl6 repl in a terminal, and #perl6 in an IRC client (or web browser) and just explore.

This language is deep. Opening src/core is like diving into the deep end of the ocean from the top of a Star Destroyer. The reward for your exploration of the language, however, is an increasing mastery of a programming language that is designed according to a human-centric philosophy and which approaches a level of expressivity that, some would argue, is unparalleled in the world of computing.