Alternate syntax on introspecting modules/classes/etc
Asked Answered
B

1

7

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.

Bourne answered 21/8, 2018 at 22:26 Comment(9)
I've done similar things, and now I just use the $object.?method() syntax: docs.perl6.org/routine/.%3F.html. It just returns Nil if $object doesn't have the method.Intrinsic
The question refers to my-method, but also talks about a sub. Unlike in Perl 5, where methods are resolved via a package, in Perl 6 they are not; instead, methods are installed in the meta-object and subs default to being lexical. Either can be marked with our, and only then do they go into the package. It's not quite clear which you want, or if you just want general "how do I do a plugin architecture in Perl 6" kind of answer.Philine
@JonathanWorthington sorry for the inconsistency! Of course it is our sub and I have fixed the post. Basically, I work around the matter by using: my multi sub-from-mod (::T Mu:U $mod, Str $sub) { T::{"&{$sub}"} } my multi sub-from-mod (Str $mod, Str $sub) { samewith( ::($mod), $sub ); } but still wonder if there is better way.Bourne
@VadimBelman I see you replied to Jonathan and updated your answer. But it's still not clear, at least to me, what you want. You wrote you "still wonder if there is a better way". But you didn't respond to Curt's solution. It seems perfect. It requires that the routine be declared as a method, not a sub, but does that matter? If so, why? Or is it because the public routine might validly return Nil? If so, perhaps can is what you need? More generally, as Jonathan wrote, is your question really a general "how do I do a plugin architecture in P6"?Splitting
@Splitting It looks like my initial mistype about methods led to bigger confusion. To make the long story short I need to get our subs from module-like objects (stashes, I guess). The .?method won't work for me because the information being collected could come from anywhere including roles and classes consuming these roles. Any component providing the info could also point to other modules where it should be collected from. Perhaps plugins model would do somehow, but I have no time for redesign. And anyway I'd like to extend my syntax knoweldge.Bourne
OK, that's me being slow to catch up. I can't much improve on what you've written. You could replace T::{"&{$sub}"} with &T::($sub).Splitting
Thanks for the correction! It looks much better. Actually, it's the closest hit for the solution I was looking for: my \T := $mod ~~ Str ?? ::($mod) !! $mod; &T::($subname) Much shorter and tidier!Bourne
That's clean. Sane P6ers may well lynch me for writing the content of this comment, but you were seeking syntax knowledge... my \Package = $; or my \Package = $ = ...; sets up a variable with the sigil slashed but the identifier still assignable which allows the following. Package &&= ::(Package); is horribly golfed, demanding a comment, and semantically different from your line but would be a viable alternative to what your ternary does in practice. And because Package has no sigil, &Package::($subname); would work. :) (Runs off to hide...)Splitting
@Splitting Now, that was confusion for a couple of seconds... Hope you have a really good hide because... well... muggles are not supposed to know about this! And to finalize the matter, then it all successfully turns into my \T = $mod && ::($mod); which is much easier to grasp when it's a single statement. And it's beautiful! 😉Bourne
S
4

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.

Splitting answered 23/8, 2018 at 3:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.