I spent a fair amount of time with Rakudo over the holiday break (see
http://dave.whipp.name/sw/perl6 for the writeup). I was generally
impressed with both the language and its implementation. There were,
unsurprisingly, a bunch of things missing. Some of these were things
that are in the spec, but not yet in Rakudo. Others are things that feel
important, but which could be added later, via modules. But one
omission, junctional collapse, appears to me to require core language
support: it is not a feature that can be added as a user module.
The term "collapse" is a metaphor taken from quantum mechanics. It is
apt, because the concept of the "values" of a junction makes sense only
in the context of the action of an operator on the junction. It is my
proposal that we add a new meta-operator to S03 that acts to apply other
operators (equality and inequality tests) to junctions:
@domain |op| $junction --> @values ## junction-collapsing "grep"
@domain !|op| $junction --> @values ## it's negated partner
The operator has the semantics of a "grep", but with the ability to
handle infinite ranges in finite time. The reason for this should become
apparent from a motivating example. Consider a blackjack hand: a set of
cards where the value of aces is either 1 or 11. The total value of a
hand can be described as a junction:
my $ace = 1 | 11;
my $seven = 7;
my @hand = $ace xx 3, $seven;
my $junc_value = [+] @hand; ## any( 10, 20, 30, 40 )
There are a bunch of possible values in the junction. The one we care
about is the largest that is not greater than 21. Using Perl6 as it
stands today, the way to extract this value is brute force:
my $concrete_value = max (0..21).grep: { $^score == $junc_value };
This will work, but doesn't scale. Imagine we wanted all the possible
values, and know nothing about their domain:
my @all_values = (-Inf..Inf).grep: { $^score == $junc_value };
This code has an obvious performance problem that would be tricky to
optimize away! And that problem is the justification of my assertion
that junctional collapse requires core language support. Using a "grep"
meta operator:
my $concrete_value = max 0 .. 21 |==| $junc_value;
my @all_values = -Inf .. Inf |==| $junc_value;
the infinite range can be handled correctly.
Adding a new meta-operator should not be undertaken lightly. I believe
that the collapse of junctions is a necessary feature of the language,
and one that cannot be added as a user-module. Furthermore, alternatives
such as a "eigenvalues" method on the Junction class fail to address the
core property of junctions that their value depends on the operator that
is used to observe them.
Another hurdle that the meta operator must overcome it to demonstrate
that it is sufficiently general. If the only values of "op" was "==",
then we wouldn't need a "meta". Also, the operator has application
beyond collapsing junctions. Some more examples:
say ^Inf |>| one 4 .. Inf; ## result == 5
say @lines |~~| /word/; ## no junction: a standard "grep"
An additional feature -- not part of my core proposal, but one that I
think would add value -- would be to allow a typename to be used as the
LHS domain, in place of an infinite range:
say ::Str |eq| any < foo bar baz >; ## match against all strings
say ::Str !|ne| all < foo bar baz >; ## same result
say ::Str !|eq| none < foo bar baz >; ## same again!
or a finite range:
subset BJ_score where { $_ == any 0 .. 21 };
say "score is { max( ::BJ_score |==| $junc_score ) // "bust" }"
I'd like to think that this proposal is an obvious way to meet a
necessary need. Even if this exact approach is not chosen, I hope to
have at least convinced you that core language support is needed to
extract the "values" of a junction; and that a "Junction::eigenvalues"
method is not the correct way to achieve this.
Dave.
ps. The reason for choosing vertical bars, |op|, as the meta-op syntax
is partly by analogy to the concept of "absolute value" that
mathematicians express using vertical bars; and partly a less-cute
distillation of a "bra-ket" suggestion I make in the writeup that I
referenced above.