Подпись ограничения в ролях в раку - PullRequest
8 голосов
/ 11 апреля 2020

Может быть, я что-то упустил, но я хотел бы знать, если есть веская причина, почему этот код должен компилироваться

role L {
  method do-l (Int, Int --> Int ) { ... }
}

class A does L {
  method do-l (Int $a, Real $b --> Str) {
    .Str ~ ": Did you expect Int?" with $a + $b
  }
}

my $a = A.new;

say $a.do-l: 2, 3.323

Это выдаст

5.323: Did you expect Int?

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

Ответы [ 2 ]

6 голосов
/ 11 апреля 2020

выдает предупреждение с внедренной подписью роли L.

Вы получите это, если префикс объявления метода будет multi:

role L {
  multi method do-l (Int, Int --> Int ) { ... }
}

При этом ваша программа отображает:

===SORRY!=== Error while compiling ...
Multi method 'do-l' with signature :(A: Int $, Int $, *%_ --> Int)
must be implemented by A because it is required by a role ...

Я хотел бы знать, если есть веская причина, почему этот код должен компилироваться [без multi]

Я думаю, что целью проекта было поддержать два понятия полиморфной c композиции:

  • Без multi, правоприменение относится только к существованию метода с правом имя ; параметры игнорируются.

  • При multi принудительное применение охватывает также имя и все параметры ( или некоторые ).

Мое личное мнение о том, есть ли веская причина для:

  • Поддержка двух разновидностей метода полиморфизма? Иногда полезно строгое соблюдение подписи. Иногда это мешает.

  • Различение их с помощью multi? Полное применение подписи требует, чтобы у реализующих классов / ролей был метод с точно та же подпись. Но что, если реализующий класс / роль хочет обработать параметр int вместо Int? Раку компромиссы. При условии, что реализующий класс / роль имеет точно совместимый метод, он может также иметь варианты. Идеальный способ передать это - добавить префиксный метод к multi.

  • Если по умолчанию используется только имя полиморфизм? Мы могли бы выбрать multi семантика по умолчанию, и пользователи должны писать префикс only, если им нужен только полиморфизм имен. Но это полностью изменило бы обычную ситуацию (т.е. игнорирование методов с заглушкой). В более общем смысле, намерение состоит в том, что Raku предоставляет широкий спектр возможностей для своих функций, от расслабленного до напряженного, и выбирает значение по умолчанию для любой данной функции, которая оценивается правильно на основе отзывов пользователей за эти годы.

Что если настройки по умолчанию не кажутся правильными? Что если существующего диапазона стриктур недостаточно? Что если одна группа думает, что мы должны go уйти, а другая думает, что мы должны go справа?

У Раку есть (imo) замечательные механизмы управления для поддержки эволюции языка, управляемого пользователем. На верхнем уровне находятся такие элементы, как архитектура кос . На нижнем уровне находятся такие элементы, как версии . Посередине находятся такие элементы, как RoleToClassApplier, которые опосредуют процесс применения роли к классу, который является точкой, в которой требуется найти требуемый метод, иначе конструкция класса потерпит неудачу. Короче говоря, если язык работает не так, как вы хотите, включая такие вещи, как ограничения, вы можете, по крайней мере, в принципе, изменить его так, чтобы он работал.

2 голосов
/ 11 апреля 2020

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

Вы можете увидеть, как роли составляются в класс здесь, в source Ракудо ($yada в основном означает $is-stubbed) :

if $yada {
    unless has_method($!target, $name, 0)
            || $!target.HOW.has_public_attribute($!target, $name) {
        my @needed;
        for @!roles {
            for nqp::hllize($_.HOW.method_table($_)) -> $m {
                if $m.key eq $name {
                    nqp::push(@needed, $_.HOW.name($_));
                }
            }
        }
        nqp::push(@stubs, nqp::hash('name', $name, 'needed', @needed, 'target', $!target));
    }
}

Итак, вы можете видеть, что logi c просто чтобы увидеть, существует ли метод с тем же именем. Определенно можно написать модуль, который обновил бы эту логику c путем увеличения метода apply () или прямой замены класса RoleToClassApplier. Однако это может быть сложно. Например, рассмотрим:

class Letter { }
class A is Letter { } 
class B is Letter { }

role Foo {
  method foo (Letter) { ... }
}

class Bar does Foo { 
  method foo (A) { 'a' }
  method foo (B) { 'b' }
}

Должны ли мы считать, что метод с заглушками правильно реализован? Но кто-то еще мог позже сказать class C is Letter, и вдруг это не реализовано. (Конечно, мы можем сказать, что для лучшей логики c потребуется, по крайней мере, идентичный или супертип, но это более ограничительно, чем необходимо для динамических c языков, IMO).

Там это не AFAICT, метод, который вызывается для ролей во время композиции, который также ссылается на класс (на самом деле, в add_method метод вообще не вызывается, поэтому нет способа написать собственную проверку в роли. (но я могу ошибаться, может быть, raiph, lizmat или jnthn меня поправят).

В этом случае я бы рекомендовал вместо того, чтобы заглушить это, просто добавить в объявление ie выражение:

role L {
  method do-l(Int $a, Int $b --> Int) {
    die "SORRY! Classes implementing role L must support the signature (Int, Int)";
  }
}

Это не захватит его при компиляции, но если в какой-то момент другой метод в L должен вызвать do-l(Int, Int) - даже если класс-составитель реализует другие сигнатуры - он будет вызван и Ошибка обнаружена довольно быстро.

...