Signaturbeschränkung in Rollen in Raku

8

Vielleicht fehlt mir etwas, aber ich würde gerne wissen, ob es einen guten Grund gibt, warum dieser Code kompiliert werden sollte

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

Dies wird ausgegeben

5.323: Did you expect Int?

Ich war neugierig, ob jemand einen Weg kennt, wie der Compiler zumindest eine Warnung mit der implementierten Signatur der Rolle auslösen kann L.

Margolari
quelle
2
Was ist Ihre erwartete Leistung? Was Sie gezeigt haben, ist das, was ich erwarten würde. Das L.do-lhat eine Methode, die nicht übereinstimmt, und in jedem Fall hat die Klassenmethode Vorrang, daher sollte auf beiden Basen As do-laufgerufen werden. Das Hinzufügen von a Intund a Realwird a sein Real.
user0721090601
1
Ah, jetzt verstehe ich. Sie fragen tatsächlich nach dem Yada-Operator ...(obwohl dies im Produktionscode sinnvoll ist, kann dies zu Verwirrung führen)
user0721090601
Sie können Rollen nicht instanziieren, wenn eine Methode gestoppt ist, aber das Instanziieren von Klassen ist kein Problem. `Klasse L {Methode yadda {…}}; sag L.new.raku`
jjmerelo
2
"Sie können Rollen nicht instanziieren, wenn eine Methode gestoppt ist" Leser können diesen Kommentar falsch interpretieren. Sie können keine Rollen instanziieren, Punkt. Sie sind nur Fragmente einer Klasse. Wenn Sie versuchen, eine Rolle zu instanziieren, geht Raku davon aus, dass Sie eine leere Klasse instanziieren möchten, die diese Rolle übernimmt, und das für Sie. Dies schlägt natürlich fehl, wenn die Rolle eine Methode enthält, die gestoppt ist, da die Durchsetzung der springende Punkt und die Bedeutung einer Methode in einer Rolle ist. Im Gegensatz dazu bedeutet Stubbing in einer Klasse nur "Ich habe diesen Code noch nicht geschrieben".
Raiph
2
@jjmerelo Ja, es ist ein Wortspiel ("untergräbt oder umgeht das Typensystem"). Der, über den wir hier sprechen ( role foo {}.newder automatisch in so etwas umgewandelt wird anon class foo does role foo {}.new), ist so transparent, dass die Leute möglicherweise nicht erkennen, dass es sich um ein Wortspiel handelt, daher mein vorheriger Kommentar. Es ist schön, dass dieses Wortspiel im Alleingang die primäre Alternative zur Vererbung und zur Delegierung ermöglicht, nämlich das Organisieren und Konstruieren von Typen mithilfe von Kompositionen , aber imo ist es wichtig, dass die Leute nicht ganz aus den Augen verlieren, dass .newes sich um ein Wortspiel handelt.
Raiph

Antworten:

6

Wirf eine Warnung mit der implementierten Signatur der Rolle L.

Sie erhalten dies, wenn Sie der Methodendeklaration Folgendes voranstellen multi:

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

Damit zeigt Ihr Programm:

===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 ...

Ich würde gerne wissen, ob es einen guten Grund gibt, warum dieser Code kompiliert werden sollte [ohne multi]

Ich denke, die Absicht des Designs war es, zwei Begriffe der polymorphen Zusammensetzung zu unterstützen:

  • Ohne dasmulti bezieht sich die Durchsetzung nur auf das Vorhandensein einer Methode mit dem richtigen Namen ; Parameter werden ignoriert.

  • Mit demmulti deckt die Durchsetzung auch den Namen und alle Parameter ( oder einige ) ab.

Meine persönliche Meinung, ob es einen guten Grund gibt für:

  • Unterstützung von zwei Varianten des Methodenpolymorphismus? Manchmal ist es hilfreich, die strikte Einhaltung der Signatur durchzusetzen. Manchmal stört es.

  • Unterscheiden sie über multi? Für die vollständige Durchsetzung der Signatur muss die Implementierung von Klassen / Rollen über eine Methode mit genau derselben Signatur verfügen . Was aber, wenn eine implementierende Klasse / Rolle einen intanstelle Inteines Parameters verarbeiten möchte ? Raku geht Kompromisse ein. Vorausgesetzt, eine implementierende Klasse / Rolle verfügt über eine genau kompatible Methode, kann sie auch Variationen aufweisen. Der perfekte Weg, dies zu vermitteln, besteht darin, einer Stub-Methode das Präfix zu geben multi.

  • Haben Sie die Standardeinstellung nur Polymorphismus? Wir hätten die multiSemantik als Standard wählen können und Benutzer ein onlyPräfix schreiben lassen, wenn sie nur Polymorphismus nennen wollten. Dies würde jedoch die übliche Situation umkehren (dh Stub-Methoden ignorieren). Im Allgemeinen besteht die Absicht darin, dass Raku eine breite Palette von Einschränkungen für seine Funktionen bietet, von entspannt bis angespannt, und eine Standardeinstellung für eine bestimmte Funktion auswählt, die auf der Grundlage des Feedbacks der Benutzer im Laufe der Jahre als richtig beurteilt wird.

Was ist, wenn die Standardeinstellung nicht richtig erscheint? Was ist, wenn das vorhandene Spektrum an Strikturen nicht ausreicht? Was ist, wenn eine Gruppe denkt, wir sollten nach links gehen und eine andere denkt, wir sollten nach rechts gehen?

Raku verfügt über (imo) bemerkenswerte Governance-Mechanismen zur Unterstützung der benutzergesteuerten Sprachentwicklung. Auf der obersten Ebene gibt es Elemente wie die Geflechtarchitektur . Auf der untersten Ebene gibt es Elemente wie versionierbare Typen . In der Mitte befinden sich Elemente RoleToClassApplier, die den Prozess des Anwendens einer Rolle auf eine Klasse vermitteln. Dies ist der Punkt, an dem eine erforderliche Methode gefunden werden muss oder die Konstruktion der Klasse fehlschlägt. Kurz gesagt, wenn die Sprache nicht wie gewünscht funktioniert, einschließlich Strikturen, können Sie sie zumindest im Prinzip so ändern, dass sie funktioniert.

Raiph
quelle
1
Wow, das ist genau die Information, nach der ich gesucht habe. Ich wünschte, ich hätte darüber in den Raku-Dokumenten für Rollen gelesen, oder vielleicht habe ich es verpasst, wenn dies nicht vorhanden ist, würde es sicherlich vielen Menschen helfen, prägnante Rollen zu schreiben, da zunächst Der Anblick, multidiesen Stummel einsetzen zu müssen, ist nicht intuitiv, aber jetzt macht es dank Ihrer Erklärung tatsächlich Sinn.
Margolari
1
@margolari Es ist gut zu hören, dass du die Informationen hast, die du brauchst. Siehe auch github.com/Raku/doc/issues/3330
raiph
2

Ich gehe davon aus, dass Sie hier fragen, warum es keine Warnung in Bezug auf das Stubbing gibt. Normalerweise muss eine Stubbed-Methode implementiert werden - aber das war's.

Sie können hier in Rakudos Quelle sehen, wie die Rollen zu Klassen zusammengesetzt sind (im $yadaGrunde bedeutet das $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));
    }
}

Sie können also sehen, dass die Logik nur darin besteht, zu prüfen, ob eine Methode mit demselben Namen vorhanden ist. Es ist definitiv möglich, ein Modul zu schreiben, das diese Logik aktualisiert, indem die Methode apply () erweitert oder die RoleToClassApplierKlasse vollständig ersetzt wird . Es könnte jedoch schwierig sein. Betrachten Sie zum Beispiel:

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' }
}

Sollten wir die Stubbed-Methode als korrekt implementiert betrachten? Aber jemand anderes könnte später sagen class C is Letterund plötzlich ist es nicht implementiert. (Natürlich könnten wir sagen, dass die beste Logik zumindest den identischen oder Supertyp erfordert, aber das ist restriktiver als für dynamische Sprachen erforderlich, IMO).

AFAICT gibt es keine Methode, die während der Komposition für Rollen aufgerufen wird und die auch auf die Klasse verweist (tatsächlich wird überhaupt keine Methode aufgerufen add_method). Daher gibt es keine Möglichkeit, einen eigenen Scheck innerhalb der Rolle zu schreiben. (aber ich könnte mich irren, vielleicht könnten Raiph, Lizmat oder Jnthn mich korrigieren).

Meine Empfehlung in diesem Fall wäre, anstatt es zu stubben, einfach eine Würfelanweisung hinzuzufügen:

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

Dies erfasst es nicht bei der Kompilierung, aber wenn zu irgendeinem Zeitpunkt eine andere Methode Laufgerufen werden do-l(Int, Int)muss - selbst wenn die komponierende Klasse andere Signaturen implementiert -, wird es aufgerufen und der Fehler wird ziemlich schnell abgefangen.

user0721090601
quelle
Ähm, ich habe nicht daran gedacht, dass Multi einen großen Unterschied macht, wie Raiph feststellte - ich sollte die Antwort vor diesem Hintergrund aktualisieren
user0721090601