Skip to content

Instantly share code, notes, and snippets.

@wimvanderbauwhede
Last active October 8, 2020 19:57
Show Gist options
  • Save wimvanderbauwhede/85fb4b88ec53a0b8149e6c05740adcf8 to your computer and use it in GitHub Desktop.
Save wimvanderbauwhede/85fb4b88ec53a0b8149e6c05740adcf8 to your computer and use it in GitHub Desktop.
The strange case of the greedy junction

The strange case of the greedy junction

An illustration of how Raku's junctions are greedy by design, and a proposal.

Raku has a neat feature called Junctions. In this short article I want to highlight a peculiar consequence of the interaction of junctions with functions: they are greedy, by which I mean that they inadvertently turn other arguments of functions into junctions. To illustrate this behaviour, I will create a pair data structure that can take two values of different types, using a closure.

enum RGB <R G B>;

# Pair Constructor: the arguments of pair() are captured
# in a closure that is returned
sub pair(\x, \y) {
    sub (&p){ p(x, y) } 
}

So pair takes two arguments of arbitrary type and returns a closure which takes a function as argument. We will use this function to access the values stored in the pair. I will call these accessor functions fst and snd:

# Accessors to get the values from the closure
my sub fst (&p) {p( sub (\x,\y){x})}
my sub snd (&p) {p( sub (\x,\y){y})}

The function that does the actual selection is the anonymous subroutine returned by fst and snd, this is purely so that I can apply them to the pair rather than have to pass them as an argument. Let's look at an example, a pair of an Int and an RGB:

my \p1 = pair 42, R;

if ( 42 == fst p1) {
    say snd p1;	#=> says "R"
}

So we create a pair by calling pair with two values, and use fst and snd to access the values in the pair. This is an immutable data structure so updates are not possible.

Now let's use a junction for one of the arguments.

# Example instance with a 'one'-type junction
my Junction \p1j = pair (42^43),R;

if ( 42 == fst p1j) {
    say snd p1j; #=> one(R, R)
}

What has happened here is that the original argument R has been irrevocably turned into a junction with itself. This happens despite the fact that we never explicitly created a junction on R. This is a consequence of the application of a junction type to a function, and it is not a bug, simply an effect of junction behaviour. For a more detailed explanation, see my article "Reconstructing Raku's Junctions".

The Raku documentation of junctions says that you should not really try to get values out of a junction:

"Junctions are meant to be used as matchers in Boolean context; introspection of junctions is not supported. If you feel the urge to introspect a junction, use a Set or a related type instead."

However, there is a FAQ that grudgingly shows you how to do it. The FAQ once again warns against doing this:

"If you want to extract the values (eigenstates) from a Junction, you are probably doing something wrong and should be using a Set instead."

However, as demonstrated by the example I have given, there is a clear use case for recovering values from junctions. It is of course not the intention that one of the values stored in the pair becomes inaccessible simply because the other value happens to be a junction.

I therefore propose the addition of a collapse function which will allow to collapse these inadvertent junction values into their original values.

if ( 42 == fst p1j) {
    say collapse(snd p1j); #=> says 'R'
}

The implementation of this function is taken from the above-mentioned FAQ, with the addition of a check to ensure that all values on the junction are identical.

sub collapse(Junction \j) {    
    my @vvs;
    -> Any \s { push @vvs, s }.(j);    
    my $v =  shift @vvs;        
    my @ts = grep {!($_ ~~ $v)}, @vvs;
    if (@ts.elems==0) {  
        $v
    } else {
        die "Can't collapse this Junction: elements are not identical: {$v,@vvs}";
    }
}

It would be nice if this functionality could be added as a collapse method to the Junction class.

@lizmat
Copy link

lizmat commented Oct 5, 2020

Is there a reason to keep this just as a gist, instead of a "proper" blog post?

@wimvanderbauwhede
Copy link
Author

Not really. The gist has the advantage that people can comment, because on my blog I don't have comments. And because I had thought that this post might result in some discussion, I thought I'd try a gist.
All I need to do is select a picture for each and then they can go on the blog.

@lizmat
Copy link

lizmat commented Oct 5, 2020

Let me know when you do this: https://rakudoweekly.blog/2020/10/05/2020-40-manifestly/ now refers to the gists, but can be updated :-)

@wimvanderbauwhede
Copy link
Author

@lizmat
Copy link

lizmat commented Oct 8, 2020

Links have been updated!

@wimvanderbauwhede
Copy link
Author

Links have been updated!

Thank you!

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