Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I call .WHY on a subroutine in a class in Perl 6?

Calling .WHY on something declared returns the special comments built around it. That's pretty cool. How can I refer to a subroutine defined in a class? Is it always hidden? I'm curious about modules that provide subroutines instead of classes (for which the answer might be "don't do it that way"). I'm mostly playing around with the limits of .WHY and how far I can take it.

#| This is the outside bit
sub outside { 137 }
put &outside.WHY; # This works

#| Class Foo is an example
class Foo {
    #| The bar method returns a number
    method bar { 137 }

    #| quux is a submethod
    submethod quux { 137 }

    #| qux is private
    submethod !qux { 137 }

    #| The baz method also returns a number
    sub baz { 37 }

    put &baz.WHY; # this works in this scope
    }

put "---- With an object";
quietly {
    my $object = Foo.new;
    put "As object: ", $object.WHY;

    # sub is not really a method?
    put "As object - bar: ", $object.^find_method( 'bar' ).WHY;

    # should this work? It *is* private after all
    put "As object - qux: ", $object.^find_method( 'qux' ).WHY;
    }

put "---- With class name";
quietly {
    put Foo.WHY;
    put "As class lookup: ", ::("Foo").WHY;
    put "As class lookup (quux): " ~  Foo.^lookup( 'quux' ).WHY;
    put "As class lookup (baz): " ~  Foo.^lookup( 'baz' ).WHY;  # nope!

    # this is the part where I need help
    put "As :: lookup: ", ::("Foo")::("&baz").WHY; #nope
    }

Here's the output:

This is the outside bit
The baz method also returns a number
---- With an object
As object: Class Foo is an example
As object - bar: The bar method returns a number
As object - qux:
---- With class name
Class Foo is an example
As class lookup: Class Foo is an example
As class lookup (quux): quux is a submethod
As class lookup (baz):
As :: lookup:

It's that last line of output I'm asking about. How can I get to a subroutine defined in a class?

like image 898
brian d foy Avatar asked Mar 10 '23 06:03

brian d foy


1 Answers

    # should this work? It *is* private after all
    put "As object - qux: ", $object.^find_method( 'qux' ).WHY;

Both of the following work:

put "As object - qux: ", $object.^find_private_method('qux').WHY;
put "As object - qux: ", $object.^private_method_table<qux>.WHY;

(find_private_method doesn't seem to be documented in p6doc, so I'm not sure if it is official API. private_method_table is documented, although not really explained.)


    # this is the part where I need help
    put "As :: lookup: ", ::("Foo")::("&baz").WHY; #nope

This one works if you declare sub baz { 37 } as our sub baz { 37 }.
You can also write the lookup more simply as:

put "As :: lookup: ", &Foo::baz.WHY;

The our declarator (explained in Synopsis 3, section "Declarators") associates a symbol with the current package – in this case, a class – so that it can be accessed from outside of it using the :: syntax.

By default, subroutines are lexically scoped – i.e. their default declarator, if none is specified, is my. (The statement sub baz { 37 } has basically1 the same effect as writing my &baz := sub { 37 } at the top of the curly-brace-delimited scope.)
By writing our sub baz { 37 }, you're telling it to use our instead of my scoping.


I'm curious about modules that provide subroutines instead of classes

Either declare it with our as shown above (in which case the user scope will have to use the fully qualified name Foo::baz), or export it into the user scope (in which case they can refer to it as just baz).

The easiest way to make a symbol exportable, is to use the is export trait:

module Foo {
    #| The baz method also returns a number
    sub baz is export(:ALL) { 37 }
}

import Foo :ALL;

say &baz.WHY;

A use statement implicitly calls import on the module in question. Since here the module is defined in the same file, we can just call import directly.

Note that you can also write sub baz is export { 37 } (without the :ALL), in which case the symbol would be imported by default whenever the class is use'd or import'ed. But in the Perl 5 / CPAN community that's considered bad practice, and letting users import individual symbols is recommended. In Perl 6, importing individual symbols doesn't work yet, so I recommend using the :ALL tag for now.


1) The only difference I'm aware of, is that in the first case the routine knows its own name for introspection purposes.

like image 78
smls Avatar answered Mar 12 '23 19:03

smls