楽の役割における署名制限


8

多分私は何かが足りないかもしれませんが、このコードをコンパイルする必要がある正当な理由があるかどうか知りたいのですが

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?

誰かが、コンパイラーが実装されたロールのシグニチャーを使用して少なくともいくつかの警告をスローする方法を知っているかどうか知りたいと思いましたL


2
予想される出力は何ですか?あなたが示したことは私が期待することです。L.do-l一致しない方法があり、どのような場合には、クラスメソッドは、その両方の拠点に、優先されるAのは、do-l呼び出す必要があります。追加IntRealなりますReal
user0721090601

1
ああ、なるほど。実際には、yada演算子について質問しています...(確かに、実動コードでは理にかなっていますが、ここでは混乱を招く可能性があります)
user0721090601

1つのメソッドがスタブされている場合、ロールをインスタンス化することはできませんが、クラスのインスタンス化には問題はありません。`クラスL {メソッドyadda {…}}; L.new.rakuと言う
jjmerelo

2
「1つのメソッドがスタブされている場合、ロールをインスタンス化することはできません」読者はこのコメントを誤って解釈する可能性があります。役割、期間をインスタンス化することはできません。それらはクラスの断片にすぎません。ロールをインスタンス化しようとすると、rakuは、そのロールを実行する空のクラスをインスタンス化することを意味し、それを代行します。当然のことながら、ロールにスタブされたメソッドがロールに含まれている場合、これは失敗します。強制はロール内のスタブされたメソッドの要点であり、意味であるためです。対照的に、クラスをスタブすることは、「このコードをまだ作成していない」ことを意味します。
レイフ

2
@jjmereloはい、それは型のしゃれです(「型システムを覆すか、回避する」)。私たちがここで話しているもの(role foo {}.new自動的にのようなものに変換されるanon class foo does role foo {}.new)は非常に透明なので、人々はそれが駄洒落であることを理解できないかもしれません。このしゃれが単独で継承と委任の主要な代替手段、つまり、代わりにコンポジションを使用して型を整理および構築することを可能にするのは素晴らしいことですが、それがしゃれであるという見解を完全に失うことのない重要な人々.newです。
レイフ

回答:


6

ロールの実装されたシグネチャで警告をスローし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] コンパイルするのに十分な理由があるかどうか知りたい

設計意図は、多形性の構成の2つの概念をサポートすることであったと思います。

  • なければmulti、強制は唯一の権利を持つメソッドの存在に関し、名前 ; パラメータは無視されます。

  • を使用するmultiと、名前とすべてのパラメーター(または一部)も適用されます。

私の個人的な見解は、次の理由があるかどうかです。

  • メソッドのポリモーフィズムの2つのフレーバーをサポートしていますか?署名を厳密に遵守することが役立つ場合があります。時にはそれが邪魔になります。

  • 経由でそれらを区別しmultiますか?完全な署名の実施には、実装するクラス/ロールがまったく同じ署名を持つメソッドを持っている必要があります。しかし、実装するクラス/ロールがパラメータのint代わりに処理したい場合はどうなりIntますか?らくが妥協。実装するクラス/ロールに完全に準拠したメソッドがある場合、バリエーションを持つことできます。これを伝えるのに最適な方法は、スタブ化されたメソッドの前にを付けることmultiです。

  • デフォルトは名前のみのポリモーフィズムですか?multiデフォルトとしてセマンティクスを選択し、only名前にポリモーフィズムのみが必要な場合は、ユーザーに接頭辞を記述させることができます。しかし、それは通常の状況を逆転させます(つまり、スタブされたメソッドを無視します)。より一般的には、Rakuはリラックスしたものから緊密なものまで幅広い機能を提供し、長年のユーザーからのフィードバックに基づいて適切と判断される特定の機能のデフォルトを選択することを目的としています。

デフォルトが正しくないと思われる場合はどうなりますか?既存の狭窄の範囲では不十分な場合はどうなりますか?あるグループが私たちが左に行くべきだと考え、別のグループが私たちが右に行くべきだと考えたらどうでしょうか?

Rakuには、ユーザー主導の言語進化をサポートするための(imo)優れたガバナンスメカニズムがあります。トップレベルには、ブレードアーキテクチャのような要素があります。最下位レベルには、バージョン管理可能などの要素があります。真ん中にはRoleToClassApplier、クラスに役割を適用するプロセスを仲介するような要素があります。これは、必要なメソッドを見つける必要があるか、クラスの構築が失敗するポイントです。要するに、言語が、狭窄などを含めて、希望どおりに機能しない場合は、少なくとも原則としてそれを変更することができます。


1
うわーこれはまさに私が探していた情報の一部です、私はこれについてロールのドキュメントで読んだか、多分私が見逃していたかと思います。multiスタブを挿入しなければならない光景は直感的ではありませんが、今ではあなたの説明のおかげでそれは実際に理にかなっています。
margolari

1
@margolari必要な情報を入手できてうれしいです。github.com/Raku/doc/issues/3330
レイフ

2

ここでは、なぜスタブに関する警告がないのかと尋ねていると思います。実際、通常、スタブ化されたメソッドを実装する必要がありますが、それだけです。

役割がクラスにどのように構成されているかは、ここRakudoのソース$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));
    }
}

したがって、ロジックが同じ名前のメソッドが存在するかどうかを確認するだけであることがわかります。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あり、突然それは実装されていません。(もちろん、最良のロジックは少なくとも同一またはスーパータイプが必要であると言うことができますが、それは動的言語IMOに必要なものよりも制限的です)。

AFAICTはありません。作成中にロールで呼び出されるメソッドであり、クラスも参照します(実際にはで呼び出されるメソッドはありませんadd_method)。そのため、ロール内に独自のチェックを記述する方法はありません。(しかし、私は間違っている可能性があり、レイフ、リズマットまたはjnthnが私を修正する可能性があります)。

この場合の私の推奨は、スタブする代わりに、単にdieステートメントを追加することです。

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)場合(たとえ構成クラスが他のシグネチャを実装していても)、それは呼び出され、エラーはかなり迅速にキャッチされます。


エラー、レイフが見つかったときにマルチが大きな違いを生むことについては考えていませんでした。それを踏まえて回答を更新する必要があります
user0721090601
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.