Search code examples
syntaxrakusymbolic-references

Alternate syntax on introspecting modules/classes/etc


I'm rewriting a framework from Perl5 to Perl6 for my work purposes. At some place I need to collect information from other modules/classes by executing a public sub they might provide; or they may not. So, it necessary to find out if the sub is present. This is not a big deal when a module is referenced directly (Foo::<&my-sub>) or by a symbolic name in a string (&::("Foo")::my-sub). But for the simplicity of it I would like to allow to pass module names as-is (lets say collector is the method collecting the info):

self.collector( Foo );

Where Foo could be the following:

module Foo {
    use Bar;
    use Baz;
    our sub my-sub { Bar, 'Baz' }  
}

And this is where I'm missing something important from Perl6 syntax because the following:

method collector ( $mod ) {
    my $mod-name = $mod.WHO;
    my @mods;
    with &::($mod-name)::my-sub {
         @mods.push: &$_();
    }
}

is currently the only way I can perform the task.

I didn't try a type capture yet though. Should work as expected, I guess. So, the question is more about extending my knowelge of the syntax.


Solution

  • The final solution from the exchange with Vadim in the comments on their question. It's arguably insane. They think it's beautiful. And who am I to argue? .oO( Haha, hoho, heehee... )

    my $pkg-arg = (Int, 'Int').pick;
    my \pkg-sym = $pkg-arg && ::($pkg-arg);
    my \sub-ref = &pkg-sym::($subname);
    

    There are two obviously useful ways to refer to a package:

    • Its symbolic name. Int is the symbolic name of the Int class.

    • Its string name. 'Int' is the string name of the Int class.

    Vadim, reasonably enough, wants a solution for both.

    In the solution in this answer I simulate the two types of argument by randomly picking one and assigning it to $pkg-arg:

    my $pkg-arg = (Int, 'Int').pick;
    

    Now we need to normalize. If we've got a symbolic name we're good to go. But if it's a string name, we need to turn that into the symbolic name.

    Vadim showed a couple ways to do this in the comments on their question. This solution uses a third option:

    my \pkg-sym = $pkg-arg && ::($pkg-arg);
    

    If $pkg-arg is a symbolic name, it'll be False. With a False LHS the && short-circuits and returns its LHS. If $pkg-arg is a string name, then the && will instead return its RHS, which is ::($pkg-arg) which is a symbol lookup using $pkg-arg as a string name.

    The upshot is that pkg-sym ends up containing a package symbolic name (or a Failure if the lookup failed to find a matching symbolic name).

    Which leaves the last line. That looks for a sub named $subname in the package pkg-sym:

    my \sub-ref = &pkg-sym::($subname);
    

    The & is needed to ensure the RHS is treated as a reference rather than as an attempt to call a routine. And pkg-sym has to be a sigilless identifier otherwise the code won't work.

    At the end of these three lines of code sub-ref contains either a Failure or a reference to the wanted sub.