Damian Conway is one of those names in the Perl and Raku world that almost doesn't need mentioning. He is one of the most prolific contributors to CPAN and was foundational in the design of Raku (then Perl 6). One of his more interesting proposals came in RFC223 on Superpositions, which suggested making his Perl Quantum::Superposition's features available in the core of the language.
In the quantum world, there are measurable things that can exist in multiple states — simultaneously — until the point in time in which we measure them. For computer scientists, perhaps the most salient application of this is in qubits which, as a core element of quantum computing, threaten to destroy encryption as we know it, if quantum supremacy is borne out.
At the end of the day, though, for us it means being able to treat multiple values as if they were a single value so long as never actually need there to only be one, at which point we get a single state from them.
In the original implementation, Dr. Conway adds two new operators, all
and any
. These converted a list of values into a single scalar value. How was this different from using a list or array? Consider the following Perl/Raku code:
my @foo = (0, 1, 2, 3, 4, 5);
We can easily access each of the values by using array notation:
print @names[0]; # 0
print @names[1]; # 1
print @names[2]; # 2
But what if we wanted to do stuff to this list of numbers? That's a bit trickier. Functional programmers would probably say "But you have map!". That's true, of course. If I wanted to double everything, I could say
@foo = map { $_ * 2} @foo; # Perl
@foo = map { $_ * 2}, @foo; # Raku
But it could also be nice if I could just say
@foo *= 2;
This is where the superposition can be helpful. Now imagine we have another array and wanted to add it to our now doubled set of values in @foo
my @bar = (0,20,40,60,80,100);
@foobar = @foo + @bar; # (12); wait what? Recall that arrays in numeric context are the number of elements, or 6 here.
Your instinctive reaction might be to say that we'd want to end up with (0,22,44,66,88,110)
which is simple enough to handle in a basic map
or for
loop (using the zip operator, Raku can do this simply as @foo Z+ @bar
). But remember what a superposition means: anything done happens to all the values, so each value in @foo needs to be handle with each value in @bar, which requires at least two loops if done via map
or for
(the cross operator in Raku can do this simply as @foo X+ @bar
). We actually want (0, 2, 4, 6, 8, 10, 20, 22, 24, 26, 28, 30, 40, 42, 44, 46, 48, 50 … )
. More difficult, then, would be to somehow compare this value:
@foobar > 10;
There is no map method we can attach to @foobar
to check its values against 10, we'd need to instead map the > 10
into @foobar
. But by using superpositioning, we can painless do all of the above with a single use of map
, for
, or anything else that generates linenoise:
use Quantum::Superposition;
my $foo = any (0, 1, 2, 3, 4, 5); # superposition of 0..5
$foo *= 2; # superposition of 0,2,4,6,8,10
my $bar = any (0,20,40,60,80,100);
my $foobar = $foo + $bar; # superposition of 0,2,4,6,8,20,22,24,26…
$foobar > 10; # True
$foobar > 200; # False
$foobar < 50; # True
$foobar < 0; # False
In fact, comparison operators are where the power of superpositions really shine. Instead of checking if a string response is an an array of acceptable responses, or using a hash
In the original proposal, there were two types of superpositions possible: all
and any
. These were proposed to work exactly as described above (creating a single scalar value out of a list of values), with their most useful feature being evident when interpreted in a boolean context. For example, in the code
my $numbers = all 1,3,5,7,9;
say "Tiny" if $numbers < 10; # Tiny
say "Prime" if $numbers.is-prime; # (no output)
For those wishing to obtain the values, he proposed the using the sub eigenstates
, which would retrieve the states without forcing it to collapse to a single one. The rest of the RFC argues why superpositions should not be left in module space, as even the Dr. Conway's work had limitations that he himself readily admitted — namely, interacting with everything that assumes a single value for a scalar and (auto)threading. The former should be fairly obvious why it would be difficult for the Quantum::Superposition module to work perfectly outside of core, because “the use of superpositions changes the nature of subroutine and operator invocations that have superpositions as arguments”.² As well, if we had a superposition of a million values, doing each operation one by one on computers with multiple processors seems silly: it should be possible to take advantage of the multiple processors. While this seems like an obvious proposition today, we must recall the multicore processors were simply not common in the consumer market when the proposal was made. (Intel's Pentium D chips didn't arrive until 2005, IBM's PowerPC970 MP in 2002.) By placing it in core, things can just work as intended and, in the rare event that a module author cares about receiving superimposed values, they could provide special support.
For the most part, RFC 233 was well received and expanded in scope. The most obvious change is the name. In the final implementation, Raku calls these superimposed values junctions. But on a practical level, two additional keywords were added, none
and one
which provide more options to those using the junctions.³A wildly different — and useful — option was added to provide syntax to create the junctions. Instead of using any 1,2,3
, one can also write 1 | 2 | 3
, and in lieu of all 1,2,3
it's possible to write 1 & 2 & 3
. Different situations might give rise to using one or the other form, which aids the Perl & Raku phiosophy of TIMTOWTDI.
One feature that did not make the cut was the ability to introspect the current states. As late as 2009, it seems it was still planned (based on this issue), but at some point, it was taken out, probably because the way that junctions work means that any methods called on them ought to be fully passed through to their superimposed values, so it would be weird to have a single method that didn't. Nonetheless, by abusing some of the multithreading that Raku does with junctions, it's still possible if one really wants to do it:
sub eigenstates(Mu $j) {
my @states;
-> Any $s { @states.push: $s }.($j);
@states;
}
Junctions are, despite their internal complexity and rarity in programming languages are something that are so well thought out and integrated into common Raku coding styles that most use them without any thought. Who hasn't written a signature with a parameter like $foo where Int|Rat
or @bar where .all < 256
? Who prefers
if $command eq 'quit' || $command eq 'exit'
to these versions? (because TIMTOWTDI)
if $command eq 'quit'|'exit'
if $command eq any <quit exit>
if $command eq @bye.any
None of these are implemented with syntactical sugar for conditionals, though it may seem otherwise. Instead, at their core, is a junction. Dr. Conway's RFC 233 is a prime example of a modest proposal that is so simultaneously both crazy and natural that, while it fundamentally changed how we wrote code, we haven't even realized it.
- I am not a physicist, much less a quantum one. I probably made mistakes here. /me is not sorry.
- Maybe there's a super convoluted way to still pull it off, but to my knowledge, he's the only person who wrote an entire regex to parse Perl itself in order to add a few new keywords, so if he deems it not possible... I'm gonna go with it's not possible.
- perhaps in the future others could be designed, such as at-least-half. The sky's the limit after all in Raku.