как создавать и экспортировать динамические операторы

У меня есть несколько классов (и потребуется еще немало), которые выглядят следующим образом:

use Unit;

class Unit::Units::Ampere is Unit
{
  method TWEAK { with self {
    .si            = True;
                   #     m·  kg·   s·   A   ·K· mol·  cd
    .si-signature  = [   0,   0,   0,   1,   0,   0,   0 ];
    .singular-name = "ampere";
    .plural-name   = "ampere";
    .symbol        = "A";
  }}

  sub postfix:<A> ($value) returns Unit::Units::Ampere is looser(&prefix:<->) is export(:short) {
    return Unit::Units::Ampere.new( :$value );
  };

  sub postfix:<ampere> ($value) returns Unit::Units::Ampere is looser(&prefix:<->) is export(:long) {
    $value\A;
  };
}

Я хотел бы иметь возможность создавать и экспортировать настраиваемые операторы динамически во время выполнения. Я знаю, как работать с EXPORT, но как мне на лету создать постфиксный оператор?


person Holli    schedule 06.11.2017    source источник


Ответы (2)


В итоге я сделал это:

sub EXPORT
{
    return %(
        "postfix:<A>" => sub is looser(&prefix:<->) {
            #do something
          }
    );
}

что очень просто.

person Holli    schedule 09.11.2017
comment
Вы не возражаете, если я добавлю эту версию в документацию EXPORT? Это изящный трюк, и я знаю, что вы имеете в виду, говоря о тревожно простом. - person piojo; 15.11.2017
comment
Нет, зачем мне это? - person Holli; 15.11.2017

Что касается первого вопроса, вы можете создавать динамические подпрограммы, возвращая подпрограмму из другого. Чтобы принять только параметр Ampere (где "Ampere" выбирается программно), используйте захват типа в сигнатуре функции:

sub make-combiner(Any:U ::Type $, &combine-logic) {
    return sub (Type $a, Type $b) {
        return combine-logic($a, $b);
    }
}

my &int-adder = make-combiner Int, {$^a + $^b};
say int-adder(1, 2);
my &list-adder = make-combiner List, {(|$^a, |$^b)};
say list-adder(<a b>, <c d>);
say list-adder(1, <c d>); # Constraint type check fails

Обратите внимание: когда я определил внутренний sub, мне пришлось поставить пробел после ключевого слова sub, чтобы компилятор не подумал, что я вызываю функцию с именем «sub». (Смотрите в конце моего ответа еще один способ сделать это.)

Теперь перейдем к самому сложному: как экспортировать одну из этих сгенерированных функций? Документация о том, что действительно делает is export, находится здесь: https://docs.perl6.org/language/modules.html#is_export

На полпути вниз по странице есть пример добавления функции в таблицу символов без возможности записи is export во время компиляции. Чтобы все вышеописанное работало, оно должно быть в отдельном файле. Чтобы увидеть пример программно определенного имени и программно определенной логики, создайте следующий MyModule.pm6:

unit module MyModule;

sub make-combiner(Any:U ::Type $, &combine-logic) {
    anon sub combiner(Type $a, Type $b) {
        return combine-logic($a, $b);
    }
}

my Str $name = 'int';
my $type = Int;
my package EXPORT::DEFAULT {
    OUR::{"&{$name}-eater"} := make-combiner $type, {$^a + $^b};
}

Вызов Perl 6:

perl6 -I. -MMyModule -e "say int-eater(4, 3);"

Как и ожидалось, на выходе будет 7. Обратите внимание, что в этой версии я использовал anon sub, что позволяет вам назвать "анонимную" сгенерированную функцию. Я понимаю, что это в основном полезно для создания лучших трассировок стека.

С учетом всего сказанного, у меня возникли проблемы с динамической установкой приоритета постфиксного оператора. Я думаю, вам нужно изменить Precedence роль оператора или создать ее самостоятельно, вместо того, чтобы позволять компилятору создавать ее за вас. Это не задокументировано.

person piojo    schedule 06.11.2017
comment
Вам не нужно вызывать & подписанные функции с &. sub foo ( &bar, $arg ){ say bar $arg }. Фактически sub foo ($a){ say $a } в основном то же самое, что и my &foo = -> $a { say $a } - person Brad Gilbert; 06.11.2017
comment
@BradGilbert Спасибо, я убрал это в примерах. - person piojo; 06.11.2017
comment
Вам нужно было удалить ., так как это требует использования & - person Brad Gilbert; 06.11.2017
comment
@BradGilbert Спасибо за все советы! Я их включил. Захват типа ::Type - это особенно круто! - person piojo; 06.11.2017