Как сделать метод "финальным" в Perl? - PullRequest
6 голосов
/ 23 февраля 2012

Мне было интересно, возможно ли убедиться, что метод в классе, который я создаю, НЕ будет исправлен обезьяной ( Исправление обезьяны ).Может ли Moose добиться этого?

Примите во внимание следующее:

{
  package Foo;
  sub hello{print "HI"}
1;
}

package main;
sub Foo::hello {print "bye"}

Foo::hello()#bye

Ответы [ 4 ]

5 голосов
/ 23 февраля 2012

После быстрого веб-исследования я обнаружил эту ветку на Perlmonks, в которой говорится:

Что касается объявления методов final, я не уверен, как бы вы это сделали, не делаячто-то действительно интересное, чтобы перехватить все дополнения в таблице символов.(Можно ли это вообще сделать?).

Я бы также предположил, что это невозможно.

Используя Moose, вы можете применять Модификаторы методов , которые позволяют вамопределить функции, которые должны выполняться до вызова функции.Я не пробовал это, но, возможно, вы могли бы определить функцию

before "hello" => sub{ # check if hello has been tampered with
                   }

Я не знаю точно, как это проверить и даже если она работает, но, похоже, стоит попробовать!

Однако я хотел бы добавить, что, поскольку perl является интерпретируемым языком, любой, кто использует ваш пакет, может просматривать и редактировать исходный код, что делает любую предосторожность обходной.

4 голосов
/ 24 февраля 2012

Perl не очень нравится концепция final подпрограмм, но вы можете попробовать. Учитывая следующее:

BEGIN {
    package final;
    $INC{'final.pm'}++;
    use Variable::Magic qw(wizard cast);
    sub import {
        my (undef, $symbol) = @_;
        my ($stash, $name)  = $symbol =~ /(.+::)(.+)/;
        unless ($stash) {
            $stash  = caller().'::';
            $name   = $symbol;
            $symbol = $stash.$name;
        }
        no strict 'refs';
        my $glob = \*$symbol;
        my $code = \&$glob;
        my ($seen, @last);

        cast %$stash, wizard store => sub {
            if ($_[2] eq $name and $code != \&$glob) {
                print "final subroutine $symbol was redefined ".
                      "at $last[1] line $last[2].\n" unless $seen++
            }
            @last = caller
        }
    }
}

Вы можете написать:

use warnings;
use strict;
{
    package Foo;
    sub hello {print "HI"}
    use final 'hello';
}

package main;
no warnings;
sub Foo::hello {print "bye"}

Foo::hello();

Который напечатает что-то вроде:

final subroutine Foo::hello was redefined at filename.pl line 9.
bye

Предупреждение выводится непосредственно перед первым вызовом переопределенной подпрограммы, а не тогда, когда она фактически переопределяется (из-за ограничений работы perl и Variable::Magic). Но это лучше, чем ничего.

Здесь есть no warnings;, поскольку perl обычно выдает предупреждение при переопределении подпрограмм. Поэтому, может быть, достаточно сказать своим пользователям use warnings. Как сказал Ларри:

В Perl нет безумного увлечения принудительной приватностью. Было бы предпочитаю, чтобы вы остались вне его гостиной, потому что вы не были не потому, что у него есть дробовик.

2 голосов
/ 31 января 2013

Фатализация переопределенных предупреждений даст вам то, что вы хотите. Но он не включит оптимизации, которые разработчик языка захочет иметь возможность оптимизировать завершенные вызовы методов, такие как индексация или встраивание.

Поскольку p5p отказывается думать о таких возможностях оптимизации (это делают perl6 и p2), это не практично. Но вам просто нужно прочитать только хэш пакета %Foo:: и его ключи. Переопределения методов будут вызывать ошибки времени компиляции, и компилятор может оптимизировать вызовы методов.

У меня есть ветка на github, которая реализует const на уровне языка для лексических выражений и пакетов. https://github.com/rurban/perl/blob/typed/const/pod/perltypes.pod#const

Это был план, но он не был поддержан: http://blogs.perl.org/users/rurban/2012/09/my-perl5-todo-list.html поэтому я раздвоил perl5. http://perl11.org/p2/

2 голосов
/ 24 февраля 2012

Вот мое решение отключить Monkey Patching , как я написал в комментарии к моему вопросу.

#./FooFinal.pm
package FooFinal;
use strict;
use warnings;
sub import { warnings->import(FATAL => qw(redefine)); }
sub hello { print "HI" }
1;

#./final_test.pl
#!/usr/bin/env perl
use strict;
use warnings;
use FooFinal;
sub FooFinal::hello {print "bye".$/}

FooFinal->hello();#bye

print 'still living!!!'

В результате ./final_test.pl умирает перед печатью "до сих пор"жить !!!».Да, это делает все методы «незащищаемыми», но все же позволяет модулю наследоваться / расширяться.И да, пользователь модуля всегда может поменять язык или сказать «нет предупреждений» :) Но все же мы громко сказали: «Вас не пригласили!»

Может быть, заголовок вопроса должен был звучать так: «Как отключить Monkey Patching в Perl?» ... Может быть, с большим чтением perlexwarn мы могли бы реализовать даже последнюю функцию ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...