Succinct MooseX:: Объявить ошибки проверки сигнатуры сигнатуры метода

Я был сторонником принятия Moose (и MooseX:: Declare) на работу в течение нескольких месяцев. Стиль, который он поощряет, действительно поможет ремонтопригодности нашей кодовой базы, но не без начальных затрат на изучение нового синтаксиса, и особенно в обучении анализу ошибок проверки типов.

Я видел дискуссию в Интернете по этой проблеме и думал, что отправлю запрос в это сообщество для:

a) известные решения

b) обсуждение того, какие сообщения об ошибках проверки должны выглядеть как

c) предложить доказательство концепции, которая реализует некоторые идеи

Я также свяжусь с авторами, но я тоже хорошо знаком с этим форумом, поэтому я решил опубликовать что-то публичное.

#!/usr/bin/perl

use MooseX::Declare;
class Foo {

    has 'x' => (isa => 'Int', is => 'ro');

    method doit( Int $id, Str :$z, Str :$y ) {
        print "doit called with id = " . $id . "\n";
        print "z = " . $z . "\n";
        print "y = " . $y . "\n";
    }

    method bar( ) {
        $self->doit(); # 2, z => 'hello', y => 'there' );
    }
}

my $foo = Foo->new( x => 4 );
$foo->bar();

Обратите внимание на несоответствие в вызове Foo:: doit с сигнатурой метода.

Сообщение об ошибке:

Validation failed for 'MooseX::Types::Structured::Tuple[MooseX::Types::Structured::Tuple[Object,Int],MooseX::Types::Structured::Dict[z,MooseX::Types::Structured::Optional[Str],y,MooseX::Types::Structured::Optional[Str]]]' failed with value [ [ Foo=HASH(0x2e02dd0) ], {  } ], Internal Validation Error is: Validation failed for 'MooseX::Types::Structured::Tuple[Object,Int]' failed with value [ Foo{ x: 4 } ] at /usr/local/share/perl/5.10.0/MooseX/Method/Signatures/Meta/Method.pm line 441
 MooseX::Method::Signatures::Meta::Method::validate('MooseX::Method::Signatures::Meta::Method=HASH(0x2ed9dd0)', 'ARRAY(0x2eb8b28)') called at /usr/local/share/perl/5.10.0/MooseX/Method/Signatures/Meta/Method.pm line 145
    Foo::doit('Foo=HASH(0x2e02dd0)') called at ./type_mismatch.pl line 15
    Foo::bar('Foo=HASH(0x2e02dd0)') called at ./type_mismatch.pl line 20

Я думаю, что большинство согласны с тем, что это не так прямо, как могло бы быть. Я реализовал взломать мою локальную копию MooseX:: Method:: Signatures:: Meta:: Method, которая дает этот результат для одной и той же программы:

Validation failed for

   '[[Object,Int],Dict[z,Optional[Str],y,Optional[Str]]]' failed with value [ [ Foo=HASH(0x1c97d48) ], {  } ]

Internal Validation Error:

   '[Object,Int]' failed with value [ Foo{ x: 4 } ]

Caller: ./type_mismatch.pl line 15 (package Foo, subroutine Foo::doit)

Супер-взломанный код, который делает это,

    if (defined (my $msg = $self->type_constraint->validate($args, \$coerced))) {
        if( $msg =~ /MooseX::Types::Structured::/ ) {
            $msg =~ s/MooseX::Types::Structured:://g;
            $msg =~ s/,.Internal/\n\nInternal/;
            $msg =~ s/failed.for./failed for\n\n   /g;
            $msg =~ s/Tuple//g;
            $msg =~ s/ is: Validation failed for/:/;
        }
        my ($pkg, $filename, $lineno, $subroutine) = caller(1);
        $msg .= "\n\nCaller: $filename line $lineno (package $pkg, subroutine $subroutine)\n";
        die $msg;
    }

[Примечание. С помощью нескольких минут сканирования кода выглядит так: MooseX:: Meta:: TypeConstraint:: Structured:: validate немного ближе к коду, который нужно изменить. В любом случае стоит вопрос об идеальном сообщении об ошибке, и кто-то активно работает над подобными изменениями или думает о них.]

Что выполняет 3 вещи:

1) Менее подробный, более простой (я обсуждал, в том числе s/Tuple//, но сейчас придерживаюсь его)

2) Включая вызывающий файл/строку (с хрупким использованием вызывающего абонента (1))

3) вместо того, чтобы признаться, умереть, поскольку, как я вижу, главное преимущество исповедания заключалось в том, чтобы найти точку входа пользователя в typechecking в любом случае, чего мы можем достичь менее подробными способами.

Конечно, я действительно не хочу поддерживать этот патч. Мой вопрос: каков наилучший способ сбалансировать полноту и лаконичность этих сообщений об ошибках, и есть ли какие-либо планы по размещению чего-то подобного на месте?

Ответ 1

Я рад, что вам нравится MooseX::Declare. Однако валидация подписи метода ошибки, о которых вы говорите, на самом деле не оттуда, а из MooseX::Method::Signatures, который, в свою очередь, использует MooseX::Types::Structured для его валидации. Всякая ошибка проверки, которую вы в настоящее время видите, не изменяется от MooseX::Types::Structured.

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

Если вам нужен способ отключить их, Moose нужно изменить для исключения исключений объектов вместо строк для ошибок проверки ограничений типа и, возможно, другие вещи. Они всегда могли захватить обратную линию, но решение о независимо от того, показывать или не отображать его или как именно форматировать его при отображении, может быть сделанным в другом месте, и пользователь будет иметь право изменять поведение по умолчанию - глобально, локально, лексически, что угодно.

То, что я собираюсь решить, - это создание фактических сообщений об ошибках проверки для сигнатуры методов.

Как уже отмечалось, MooseX::Types::Structured делает фактическую проверку Работа. Когда что-то не удается проверить, его задача - создать исключение. Эта Исключение в настоящее время является строкой, поэтому не все это полезно, когда желая построить красивые ошибки, так что необходимо изменить, аналогично проблеме с трассировкой стека выше.

Как только MooseX:: Types:: Structured выбрасывает структурированные объекты исключений, которые могут выглядят несколько как

bless({
    type => Tuple[Tuple[Object,Int],Dict[z,Optional[Str],y,Optional[Str]]],
    err  => [
        0 => bless({
            type => Tuple[Object,Int],
            err  => [
                0 => undef,
                1 => bless({
                    type => Int,
                    err  => bless({}, 'ValidationError::MissingValue'),
                }, 'ValidationError'),
            ],
        }, 'ValidationError::Tuple'),
        1 => undef,
    ],
}, 'ValidationError::Tuple')

у нас было бы достаточно информации для фактической корреляции отдельных внутренние ошибки проверки с частями сигнатуры в MooseX::Method::Signatures. В приведенном выше примере и учитывая вашу подпись (Int $id, Str :$z, Str :$y), было бы достаточно легко узнать что сам внутренний Validation::MissingValue для второго элемента кортеж для позиционных параметров должен был предоставить значение $id, но не смог.

Учитывая, что будет легко создавать ошибки, такие как

http://files.perldition.org/err1.png

или

http://files.perldition.org/err2.png

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

Ничего из этого на самом деле сложно - это просто нужно делать. Если кому-то захочется помочь с этим поговорите с нами в #moose на irc.perl.org.

Ответ 2

Method::Signatures::Modifiers - это пакет, который надеется исправить некоторые проблемы MooseX::Method::Signatures. Просто use его перегрузить.

use MooseX::Declare;
use Method::Signatures::Modifiers;

class Foo
{
    method bar (Int $thing) {
        # this method is declared with Method::Signatures instead of MooseX::Method::Signatures
    }
}