perl6 correctly passing a routine into an object variable

  • A+
Category:Languages

I need to pass some code from an external program into a class.

In a generic module I have (for simplicity reduced to silliness)

class A {    has &.hl;    submethod BUILD( :&!hl ) {} } 

Elsewhere in a program, I have

use A; my &hl = -> $st {     my $p = shell "hl $st", :in,:out;    $p.out.slurp }; my $statement = 'my $perl6-variable = "Helloooo";' my $first = &hl($statement); my A $a .= new(:&hl); my $second = $a.hl( $statement ); 

$first will be processed and will contain the expected results.

At $second, I will get a runtime error

Too many positionals passed; expected 1 argument but got 2 

Clearly the routine in the class is being provided both the invocant and the parameter $s.

Rewriting the class to provide a custom accessor:

class A {    has &!hl;    submethod BUILD( :&!hl ) {}    method process-it( Str $s --> Str ) { &!hl( $s ) } } # elsewhere my $second = $a.process-it( $statement ); 

Then both $first and $second run without error and will contain the same results.

When hl is accessed inside the class, no invocant is added, but if it is not declared as &.hl then it is not visible outside the class.

My question is therefore: Is there another way to create a public object code variable that does not automagically add the invocant as a variable to the code? Other than creating a separate accessor method.

Here is short bash script hl for illustration

#! /bin/bash echo '<div class="statement">'$1'</div>' 

Here is a full Perl 6 program

use v6.c;  class A {     has &!highlighter; # also tried with has &highlighter     submethod BUILD( :&!highlighter ) {}     method process-it( Str $s --> Str ) {        &!highlighter( $s )     } }  sub MAIN() {     my @strings = 'my $v = "Hello World";', 'my $w = $v.perl;';     my $proc;     my $proc-supply;     my &highlighter = -> $s {         my $p = shell "./hl '$s' ", :in,:out;         $p.out.slurp     }      for @strings {         say .&highlighter     }     my A $a .= new(:&highlighter);     for @strings { say $a.highlighter($_) }     # own accessor     for @strings { say $a.process-it($_) } } 

 


The problem is that the accessor returns the attribute, that happens to be a Callable. Only then do you want to call the return value of the accessor with parameters. This is essentially what you're doing by creating your own accessor.

You don't have to actually create your own accessor. Just add a extra parentheses (indicating you're calling the accessor without any extra arguments), and then the parentheses for the values you actually want to pass:

class A {     has &.a = *.say;  # quick way to make a Callable: { .say } } A.new.a()(42);        # 42 

Or if you don't like parentheses so much, consider the method invocation syntax, as timotimo pointed out:

A.new.a.(42);         # 42 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: