How to return the values in a junction as an array?
Asked Answered
Y

3

10

Define a junction my $j = 1 | 2 | 3 | 4 | 5, now I want to get an array of its value [1 2 3 4 5], how should I implement this?

I tried $j.values but Perl6 gave me the whole junction as an element: [any((1), (2), (3), (4), (5))].

Ynez answered 23/4, 2017 at 7:14 Comment(1)
According to the documentation: "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."Sapers
G
6

As Håkon Hægland already pointed out, this is not something you're supposed to do:

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.

 -- docs.perl6.org/type/Junction

However, it is possible.

First, you can use authothreading (ie the automatic evaluation of each branch of a junction when passed to a function that expects an argument of type Any):

sub unjunc(Junction $j) {
    gather -> Any $_ { .take }.($j);
}

Second, you can poke into the guts and manually extract the values:

sub unjunc(Junction $j) {
    multi extract(Any $_) { .take }
    multi extract(Junction $_) {
        use nqp;
        my $list := nqp::getattr($_, Junction, '$!storage');
        my int $elems = nqp::elems($list);
        loop (my int $i = 0; $i < $elems; $i = $i + 1) {
            extract nqp::atpos($list, $i);
        }
    }
    gather extract $j;
}

If your junction is non-recursive (ie does not contain other junctions you want to flatten), the latter approach can be simplified:

my $j := 1|2|3;
say nqp::p6bindattrinvres(nqp::create(List), List, '$!reified',
    nqp::getattr($j, Junction, '$!storage'));
Gereld answered 23/4, 2017 at 8:5 Comment(1)
The nqp variants are broken. In order to build the list without depending on Rakudo's implementation details directly, you need to thread.Juridical
R
10

This is intentional, as far as I know.

Imagine $j containing a Junction of hashes: then $j.values would be a junction of Seq's, not the hashes themselves.

If you want the array of a junction, then maybe you should start from an array, and build a junction out of that:

my @a = 1,2,3,4,5;
my $j = any(@a);

If you really want to go the Junction -> Array way, you could, but it would involve using nqp, and that's something I would not recommend in userland code.

Refill answered 23/4, 2017 at 8:7 Comment(1)
This can be simplified even further: my $j = any my @a = 1,2,3,4,5;Tripper
G
6

As Håkon Hægland already pointed out, this is not something you're supposed to do:

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.

 -- docs.perl6.org/type/Junction

However, it is possible.

First, you can use authothreading (ie the automatic evaluation of each branch of a junction when passed to a function that expects an argument of type Any):

sub unjunc(Junction $j) {
    gather -> Any $_ { .take }.($j);
}

Second, you can poke into the guts and manually extract the values:

sub unjunc(Junction $j) {
    multi extract(Any $_) { .take }
    multi extract(Junction $_) {
        use nqp;
        my $list := nqp::getattr($_, Junction, '$!storage');
        my int $elems = nqp::elems($list);
        loop (my int $i = 0; $i < $elems; $i = $i + 1) {
            extract nqp::atpos($list, $i);
        }
    }
    gather extract $j;
}

If your junction is non-recursive (ie does not contain other junctions you want to flatten), the latter approach can be simplified:

my $j := 1|2|3;
say nqp::p6bindattrinvres(nqp::create(List), List, '$!reified',
    nqp::getattr($j, Junction, '$!storage'));
Gereld answered 23/4, 2017 at 8:5 Comment(1)
The nqp variants are broken. In order to build the list without depending on Rakudo's implementation details directly, you need to thread.Juridical
J
0

There's a way to thread a junction of Mu so as to build an Array of eigenstates with their containers preserved. Because Mu is a supertype of the Any that autothreading is oriented towards, we can't depend on that feature while covering all possible eigenstate types, however.

Mu.ACCEPTS has a multi method ACCEPTS(Mu:U: Junction:D) is default candidate to allow for smartmatches of junctions against any type object. This allow for a manual threading if fed a thunk that can recurse over ACCEPTS to be chained with CALL-ME, which can thread over its invocant:

class Unison is Mu is repr<Uninstantiable> {
    method CALL-ME(Mu $topic is raw --> Array:D) {
        my @list;
        proto sub push(Mu) {*}
        multi sub push(Mu $topic is raw) { @list.BIND-POS(@list.elems, $topic) }
        multi sub push(Junction:D $junction) { self.ACCEPTS($junction).(&push) }
        self.ACCEPTS($topic).(&push);
        @list
    }

    multi method ACCEPTS(Mu $topic is raw) {
        -> &accept { accept $topic }
    }
}

say Unison(1 | 2 | 3 | 4 | 5);    # OUTPUT: [1 2 3 4 5]
say Unison((1 | (2, 3) & 4) ^ 5); # OUTPUT: [1 (2 3) 4 5]

ACCEPTS must not be overridden in its entirety, but since Mu is the parent and not Any, just the one Mu candidate can cover its bases. This will only work on a type object invocant, so this is given the Uninstantiable REPR.

Juridical answered 2/12, 2022 at 16:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.