Skip to content

Instantly share code, notes, and snippets.

@masak

masak/post.md Secret

Last active August 29, 2015 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masak/bd47a4022aa90a58e55a to your computer and use it in GitHub Desktop.
Save masak/bd47a4022aa90a58e55a to your computer and use it in GitHub Desktop.
advent post day 3: cap your junctions

"With great power comes great responsibility." These days I muse on how this applies not just to movie superheroes, but to programming language features as well.

It happens all over the place. Database connection? Awesome. Well, until you become the victim of an SQL injection or a SELECT N+1 problem. Regular expressions? Also awesome... until someone uses it to parse HTML and gets a visitation from elder non-beings beyond the fabric of reality. Ouch.

Of course, we still like our powerful features. But we need to learn to contain them, to harness them. The awesome should work for us and the purpose we have set it to. It should not work for attackers, elder non-beings, or Murphy. We need to make sure the awesome doesn't leak beyond its designated boundaries.

In 2009, Matthew Walton blogged about junctions. His explanation of what makes junctions exciting is great, and I agree with it. Today I would simply like to add the following point: junctions are so awesome that they should be contained.

Let's demonstrate the problem real quickly.

> my @list = 3..7
3 4 5 6 7
> my $answer = all(@list) > 0;

Quick, what value ends up in $answer? Well, all the values in @list are indeed greater than 0, so...

all(True, True, True, True, True)

Aw, dang. Not again.

Just to be clear, this is by spec, and correctly implemented and everything. But unless you ask specifically for Perl 6 to collapse the junction, it just keeps on going. Keeps on going, like a juggernaut, through walls and oceans, propagating through your program whether you want to or not.

What you have to do then is to collapse the junction, putting a cap on all that awesome. The junction is useful as long as you're doing the junctive computation itself (in this case all(@list) > 0), but immediately after that, we'll want to collapse back into the normal, sane, non-junctive world.

> ?$answer      # symbolic prefix
True
> so $answer    # listop prefix
True
> $answer.Bool  # coerce method
True

I will hasten to add that the most common use of junctions don't suffer from this problem, namely junctions in if statements:

if all(@list) > 0 {
    ...
}

The if statement itself provides a natural cap to the junction. It does its own boolification of it under the hood, but more importantly, the junction value doesn't stick around. We only see the effects of the boolean value of the junction.

No, the real risk comes when you're a library writer, and you provide routines that happen to compute things using junctions:

sub all-positive(@list) {
    all(@list) > 0;     # DANGER
}

That's where you want to remember the old tired adage. With great power... comes... right, that's right. So you cap your junction before you let it run out into the wild and do untold structural damage to people, cattle, and those cute fluffy dogs that would otherwise suffer Puppy Death By Junction.

sub all-positive(@list) {
    so all(@list) > 0;
}

Yes. That's better.

If you declare your return type, you can get a runtime error for leaving out the so.

sub all-positive(@list --> Bool) {
    all(@list) > 0;
}

say all-positive([1, 2, 3]); # Type check failed for return value; expected 'Bool' but got 'Junction'

Maybe that's an argument for declaring return types for your routines. And maybe some day we'll catch that one at compile-time, too.

Cap your junctions as early as possible.
— masak's Rule of Responsible Junction Use

The longest I can remember legitimately keeping junctions around in an uncollapsed state was when storing junctions of regexes in a hash as part of this password-analyzing script. Even that's not an exception to the above rule, though — note that the first thing that happens to the junctions in the subsequent code is that they become the rhs in a smartmatch statement, and then get capped by an if statement. So even here, they don't escape out into the wild.

Code responsibly. Cap your junctions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment