Synopsis 14:ロールと Parametric Types [ DRAFT ]
Larry Wall <larry@wall.org> Tim Nelson <wayland@wayland.id.au> Jonathan Worthington <jnthn@jnthn.net>
Created: 24 Feb 2009 (extracted from S12-objects.pod) Last Modified: 10 Jul 2009 Version: 9
この概要は元来 A12 で論じられたロールとパラメータを伴う型を論じる。
クラスは主にオブジェクト管理を担当している、そしてただ第二にソフトウェア再利用の責任を持っている。 Perl 6 に、ロールがソフトウェア再利用を管理することについての仕事を引き継ぐ。 あなたがどのようにそれを見ることを望むかについて、依存して、ロールがデフォルト実装、あるいは一般的なメソッドのセットで部分的なクラス、あるいはインタフェースのようだ、そして(彼・それ)らの対応データ、あるいはクラスがコンパイル時間で終了した。
ロールは、コンパイル時間に、クラスへと落ち着いているかもしれない、その場合あなたは相いれないメソッドの自動的な発見を得る。 ロールが同じく余分の能力で匿名の派生クラスを産み出すためにランタイムにクラスあるいはオブジェクトの中に混ぜられるかもしれない、しかしこの場合相いれないメソッドが静かに新しいロールによってオーバーライドされる。 どちらの場合も、クラスがインスタンス化に必要だ - ロールが直接インスタンスを作られないかもしれない。
ロールがわずか1つのクラスのように宣言されるでrole キーワード:
role Pet {
method feed ($food) {
$food.open_can;
$food.put_in_bowl;
self.eat($food);
}
}
ロールがクラスから財産を相続しないかもしれないかもしれない、しかし他のロールで構成されているかもしれない。 しかしながら、とこの「旧友」合成ではないはクラス作文時間まで評価した。 これは、もし2つのロールが同じ旧友を連れて来るなら、競合がないことを意味する - それはただクラスがそれ自身旧友でロールをはく奪した、そしてそれぞれのロールがそうしないかのようにだ。 ロールが決して合併のそのメソッドにかかわらずそれ自身と対立しないかもしれない。 それがクラスであるかのように、2の相いれない旧友にロールを持って来るロールがそれらを解決するかもしれない。 この解決は、クラスがそれ自身の解決を供給しないなら、クラスによって受け入れられる。 もし2つの異なったロールが2つの異なった方法で同じ旧友競合を解決するなら、それらのロールは競合で(彼・それ)ら自身であって、そして「さらに多くによって得られた」ロールあるいはクラスによって解決されなくちゃならない。
それがクラスへと落ち着くまで、ロールがそれ自身の型を知らない。
その主な型の Any 投稿(そんなものけれども::?CLASS)汎用体、何もであるように参照はそうするはずだself あるいはインボカントの型。 あなたは、型として、しかし制約だけのために、ロール名を実際のオブジェクトを宣言するために使うことができない。 (しかしながら、もし、それがクラスであるかのように、あなたがロールを使うなら、 infelicities のためにテストまでのロールを強制する方法にその旧友作文を提供するロールを構成する匿名のクラスが生成される。)
もしロールがそれらを定義しないでただメソッドを宣言するなら、それはインタフェースに悪化する:
role Pet {
method feed ($food) {...}
method groom () {...}
method scratch (:$where) {...}
}
これらのメソッドがクラス作文時間に利用可能にならなくちゃならない間に、それらが「もう1つのそれ自身クラスでのロールあるいは若干のスーパークラス」の何によってでも供給されるかもしれないことに注意を払え。 我々は他のロールあるいはクラスから来るメソッドを知っている、しかし、もしそれらが開いているなら、我々は必ず我々のスーパクラスによって供給されたメソッドの完全系を知るか、あるいはワイルドカード委任に頼らない。 しかしながら、クラス生成するものはただ現在宣言されたスーパークラスメソッドあるいは非ワイルドカードのメソッドが利用可能であるだろうと想定することを許される。 切り株が欠けているメソッドの宣言を「供給する」ために常にどこか(に・で)インストールされ得る。
ロールが属性を持っているかもしれない:
role Pet {
has $.collar = Collar.new(tag => Tag.new);
method id () { return $.collar.tag }
method lose_collar () { undefine $.collar }
}
ロールの中でhas 宣言詞が常にクラスの観点から宣言を示す。 そのために私的な属性が使用方法を宣言したhas ロールにではなく、クラスに私的だ。 あなたはクラスからさえ隠される属性を宣言することを望むかもしれない;(クラスの例毎に存在するであろう)完全に私的なロール属性がこのように宣言されるかもしれない:
my $!spleen;
私的な属性がそうであるそんなものの名前でレキシカルスコープな常に見なした。 もしロールが個人の語彙の品目を宣言するなら、それらの品目は語彙の有効範囲規則の性質のためにロールに個人だ。 このような項目への Accessors はクラスに輸出されるかもしれない、しかしこれはデフォルトじゃない。 特に、ロールが言うかもしれない
trusts ::?Class;
許すためにself!attr() ロールのへのアクセス$!attr クラスと一緒のあるいは他のロールからの変数がクラスへと構成した。 私的なアクセス機構の間の競合が合成時間に同じく捕えられる、しかしもちろん見なす必要がない、スーパクラスが、誰からも現在のクラス(あるいは信頼できるクラス)の外(に・で)まったく私的なアクセス機構を呼び出すことができない。
(私的なアクセス機構は決して仮想じゃなくて、そして我々自身のもの以外の信用されたスコープからもし呼び出されるなら修飾された実装に違いない。 すなわち、それはいずれかだself!attr() あるいは$obj!TrustsMe::attr().)
ロールが同じく共有されたメソッドを区別するかもしれない
has method foo ... method foo ... # same
nonsharedな私的なメソッドから:
my method !foo ... my method foo ... # same, but &foo is aliased to &!foo
一般に、けれども、あなたはただレキシカルにスコープ宣言された sub を使うだろう。
my sub foo ...
[ Conjectural :、例えば私的な sub をクラスに入れるために
our sub !foo ...
]
ロールが権限を委ねるという決定を入手することができる:
role Pet {
has $groomer handles <bathe groom trim> = hire_groomer();
}
これが3つのメソッドをクラスに入れる注目$groomer. それと対照的に「my $!groomer」ただそれ自身属性での3つのメソッドがロールに私的であると書くだろう.
ロールが、それが実装の詳細だと見なされるとき、そのクラスで追加の継承を申告することを許される:
role Pet {
also is Friend;
}
クラスが、このように、ロールを動詞「そうする」と合体する:
class Dog is Mammal does Pet does Sentry {...}
あるいは、等しく、クラスクロージャのボディーの中で:
class Dog {
also is Mammal;
also does Pet;
also does Sentry;
...
}
あるいは
class Dog {
also is Mammal does Pet does Sentry;
...
}
ロールの間に注文依存がない。
クラスの明示的なメソッド定義は同一名のどんなロール定義でも隠す。 ロールメソッドが今度は他のクラスからメソッドが継承した何でも隠す。
もしロールの間にメソッド名競合がないならそれから、(あるいはクラスと一緒に)、それぞれのロールのメソッドはクラスでインストールされ得る。 もし、しかしながら、2つのロールが同一名のメソッドを開始しようとするなら、クラスの構成は失敗する。 (2has 同一名の属性は、公共あるいは個人か否かにかかわらず、常に合成失敗だ。
ロール - 私的な属性はこれから、そして作文の観点を免除されていて、おのおののそのような属性にスロットを割り当てることを除いて、存在しさえしない。)
メソッド競合を解決するいくつかの方法がある。 最初はただ、多分どのロールメソッドを呼び出すべきか理解して、相いれないロールメソッドをオーバーライドするクラスメソッドを書くはずだ。
代わる代わる、もしロールのメソッドが宣言されるならmultiそれらそれらロングネームに基づいてあいまいさを排除されることができる. もしロールがそれらを宣言することを忘れるならけれども多、あなた力がそうすることができる多ロールのメソッドに、作成されているクラスで: proto 切り株を取り付けることによって、
proto method shake {...}
(この宣言はそうする必要がない先に起こるdoes 逐語的に、ロールが、我々が、どのポイントにクラスがロールをオーバーライドするつもりである方法と同様、どのロールが一つの論理演算で一緒に落ち着いているはずであるか知っているかについて、クラス定義の終わりまで実際に落ち着いていないから文節。)
もし多失敗であるなら、 proto メソッドは呼び出されるだろう:
proto method shake { warn "They couldn't decide" }
ミキシンが一緒にされる実行時does そしてbut.does 2進法の演算子は(もし必要なら)新しい匿名のクラスを得て、そしてそれにオブジェクトをバインドする mutator だ:
$fido does Sentry
does インフィックス演算子は結合じゃない、それでこれは構文エラーだ:
$fido does Sentry does Tricks does TailChasing does Scratch;
権限:、しかしながら、言え
$fido does Sentry; $fido does Tricks; $fido does TailChasing; $fido does Scratch;
そしてそれが左側を返すから、あなたは同じく言うことができる:
((($fido does Sentry) does Tricks) does TailChasing) does Scratch;
コンパイル時ロール作文と異なり、、新しいレベルの継承を持っている新しいミキシンの上のこれらの層のそれぞれ、親しい年がいった Fido のために新しい匿名のクラスを作ること、それでそれ.chase メソッドからTailChasing 隠れる.chase メソッドからSentry.
あなた容器同じくミキシンロールの前もって構成されたセット:
$fido does (Sentry, Tricks, TailChasing, Scratch);
これはロールの新しいセットの間で衝突のために競技場を平らにするだろう、そして1つのいっそう匿名のクラス以上の何の創造も保証しない。 このようなロールはまだそれ自身と対立することができない、しかしそれは親クラスでその前のメソッドを隠すことができる、そして対立することの計算は混ぜられて再びロールのセットのためにされる。 もしあなた何もできないと考える否定的思考型のコンパイル時作文、それが少なくとも関係している新しいロールの間コンパイル時作文に近付くから、我々が強く実行時ミキシンにこのアプローチを推奨するなら。
ロールが適用されたでdoes 括弧で初期化演算子でパラメータ化されているかもしれない、しかしロールが正確にミキシンクラスへの1つの属性を供給する場合に限り:
$fido does Wag($tail); $line does taint($istainted);
括弧で括られた(人たち・もの)がサブルーチンあるいはメソッドが呼び出すではないを組織するとは、注意しろ。 それは一つのプロパティを含んでいるロールのために構文を初期化してただ特別だ。
供給された初期化演算子は属性の型に強制されるだろう。 実際の型名前:について部分的にこの初期化演算子が何にでも加えてだ、そうな正方形の括弧で供給されたパラメータを伴う型が見なしたことに注意を払え
$myobj does Array[Int](@initial)
プロパティがこのようにロールによって定義される:
role answer {
has Int $.answer is rw = 1;
}
プロパティはそれから使って混ぜられるか、あるいは、代わりに、応用されることができるbut 演算子。 but そうである好むdoesけれども、コピーを作って、そしてオリジナルの unmodified を去って、その代わりに、それの中に人と交わる。 それで:
$a = 0 but answer(42)
本当に何かが好むことを意味する:
$a = ($anonymous = 0) does answer(42);
それが本当に意味する:
(($anonymous = 0) does answer).answer = 42; $a = $anonymous;
なぜがあるかであるbut 演算子。
もしあなたがそれが右側でロールじゃない何かを置いたならdoes あるいはbut 演算子それから匿名のロールがその値を返す一つのメソッドを含んでいて自動的に生成されるだろう。 メソッドの名前は、 RHS で供給された値に .WHAT.perl をすることによって、決定される。 生み出されたロールはそれから、オブジェクトに混ぜられる。 例:
$x does 42
同等の値:だ
$x does role { method Int() { return 42 } }
ロールが属性とそれで記憶域を持っていないとは、注意しろ;もしあなたがそれを欲するなら、あなたはその代わりに使うべきだ:
$x does Int(42)
それは Int ロールとイニシャライズ、一つの記憶域の場所、でそれが宣言する Int を42と混ぜて、そして左辺値アクセス機構を供給する。
値が列挙の名前に、そして結果として文字列化する列挙の上にその .WHAT に注意しろ:
0 but True
同等の値:だ
0 but role { method Bool() { return True } }
そしてそれで結果として生じている値はブールのコンテキストで真であると見なされるだろう。
シングルで多数のロールを構成することに対して、リスト構文does あるいはbut
それらを投入することによって、リストが同じくここで適用される。 それで:
42 but ("the answer", False)
同等の値:だ
42 but (role { method Str() { return "the answer" } },
role { method Bool() { return False } })
それはあなたにコンテキストに敏感な帰りの値を作るぎっしり詰まった方法を与える。 何かが好むように、多数のロールが一人の人よりむしろ生み出されるとは、注意しろ:
42 but (True, False)
標準的なロール合成意味規則の結果として(2つのロールがメソッド Bool を供給しようとする両方ともであるから)失敗するだろう。
トレイトはコンテナあるいはクラスのように、申告されている何か(declarand)に適用されたただプロパティ(ロール)だ。 トレイトを普通のプロパティよりいっそう永久に思われさせるのは項目自身の宣言だ。 プロパティを加えることに加えて、トレイトが同じく副作用を持つことができる。
トレイトは一般に、「そうである」キーワードで、しかし常にではなく応用だ。 「xxx である」トレイトのためにトレイト調教師を定義するために、このようなプロパティロールの中に1隻以上の多潜水艦を定義しろ:
role xxx {
has Int $.xxx;
multi trait_mod:<is>(::?CLASS $declarand where {!.defined}, :$xxx!) {...}
multi trait_mod:<is>(Any $declarand, :$xxx!) {...}
}
それからそれはトレイトとして関数を缶詰めにする。 行儀が良いトレイト調教師が言うだろう
$declarand does xxx($arg);
どこか(に・で)内部に正確にメタデータを declarand の上に置くために。 クラスの缶詰関数からロールとしてマッチしているパラメータ型の話になると、あなたは同じく言うことができる:
class MyBase {
multi trait_mod:<is>(MyBase $declarand where {!.defined}, MyBase $base) {...}
multi trait_mod:<is>(Any $declarand, MyBase $tied) {...}
}
これらはコントロールもしをとらえるMyBase 欲するからそれがどのようにクラスあるいはコンテナを何かによって使われさせるかのキャプチャ制御。 けれども通常あなたはただそれに一般的なデフォルトを呼び出させることができる:
multi trait_mod:<is>($declarand where {!.defined}, $base) {...}
それは付け加える$base クラスの「isa」リストに$declarandあるいは
multi trait_mod:<is>(Any $declarand, $tied) {...}
それは中にコンテナ declarand の「タイ」型を実装型にセットする$tied.
たいていのトレイトが本当にただ副詞の対であるそれ、存在の代わりだ開始するコロンまでに、何かであり得た(希望を抱いて)いっそう読める「助けている動詞」によって開始される好む「is", or " will", or " can", or " might", or " should", or " does」. 解析される Any トレイト動詞が trait_mod:<が > であるのと同じように同じように定義されるかもしれない。
ここ(で・に)ある」will」(構文的な構文糖であること)が、「そうである」に戻って:ただ権限を委ねる
multi sub trait_mod:<will>($declarand, :$trait) {
trait_mod:<is>($declarand, :$trait);
}
他のトレイトは単一語で応用であって、そして特別なパージングを必要とする。 例えば、「as」トレイトはこれとほぼ同じ確定だ:
role as {
has ReturnType $.as;
multi sub trait_mod:<as>($declarand, ReturnType $arg) is parsed /<typename>/ {
$declarand does as($arg);
}
...
}
すべて同じクラスで平らになるコンパイル時ロールと異なり、コンパイル時トレイトはミキシンロールのように、一度に1応用だ。
あなたは、実際、ランタイムにトレイトをオブジェクトに適用することができる、しかしもしあなたがそうするなら、それはただ普通のミキシンロールに過ぎない。 権限:が適切な(人たち・もの)を呼び出すtrait_mod:<isあなた自身もしあなたがそれが何かに余分の悪ふざけをすることを望むなら () > ルーチン。 それがコンパイル時間にそうするであろうように、コンパイラはランタイムにあなたのためにそれを呼び出さないだろう。
上の宣言が新しいトレイト auxilliaries をインストールするには不十分である、あるいは上記のマクロ定義がレキシカルスコープなであるときからのユーザーの文法の中へのそして宣言での動詞がただロール鮮明度の終わりに拡張することに注意を払え。 ユーザーのレキシカルスコープはどうにかして、それが正確に解析されることができる前に、新しい構文を開始している proto 宣言を処理して、(あるいは輸入した)に違いない。 (これは存在する前の構文にそんなものを用いないけれどもisもちろん。)
ロールの主な型はデフォルトまでに一般的だ、しかしあなたは型パラメータを使って明示的に同じく他の型を parameterize することができる:
role Pet[::Petfood = TableScraps] {
method feed (Petfood $food) {...}
}
(この場合あなたが使ってはならないことに注意を払え::内部の宣言でのペットフード、あるいはそれが実際の食料品のパラメータについてタイプするために型を再び拘束するだろう。)
もしあなたがロングネームについて部分的にロール属性の初期値を parameterize して、もしあなたがパラメータがそうであることを望まないなら、二重のセミコロンを置くことは見なしたと確信していることを望むなら:
role Pet[::ID;; $tag] {
has ID $.collar .= new($tag);
}
あなたはただ parameterize しなくてもよい、型;何にでも値は素晴らしい。 我々が誰かの名前をとって、そしてそれらを歓迎するロールの中に「出迎える」メソッドを要因から外すことを望んだと想像しろ。 我々は挨拶でそれを parameterize することができる。
role Greet[Str $greeting] {
method greet() { say "$greeting!"; }
}
class EnglishMan does Greet["Hello"] { }
class Slovak does Greet["Ahoj"] { }
class Lolcat does Greet["OH HAI"] { }
EnglishMan.new.greet(); # Hello
Slovak.new.greet(); # Ahoj
Lolcat.new.greet(); # OH HAI
同様に、我々は要請のためにロールをすることができた。
role Request[Str $statement] {
method request($object) { say "$statement $object?"; }
}
class EnglishMan does Request["Please can I have a"] { }
class Slovak does Request["Prosim si"] { }
class Lolcat does Request["I CAN HAZ"] { }
EnglishMan.new.request("yorkshire pudding");
Slovak.new.request("borovicka");
Lolcat.new.request("CHEEZEBURGER");
悲しいことに、スロバキアの生産はここで吸う。 Borovicka は単語の主格形だ、そして我々は対格の中にそれを辞退する必要がある。 けれども若干の言葉がそれのことを気にかけない、そして我々はそれらをすべての供給にしなければならないことを望まない変わる. 感謝すべきことに、あなたは同じ短い名前と異なったシグネチャーで多くのロールを書くことができる、そしてマルチディスパッチがあなた(それはマルチ sub によって使われた正確に同じディスパッチアルゴリズムだ)のために右の(の・もの・人)を選ぶだろう。 それで我々は書くことができる:
role Request[Str $statement] {
method request($object) { say "$statement $object?"; }
}
role Request[Str $statement, &transform] {
method request($object) {
say "$statement " ~ transform($object) ~ "?";
}
}
module Language::Slovak {
sub accusative($nom) {
# ...and before some smartass points it out, I know
# I'm missing some of the masculine animate declension...
return $nom.subst(/a$/, 'u');
}
}
class EnglishMan does Request["Please can I have a"] { }
class Slovak does Request["Prosim si", &Language::Slovak::accusative] { }
class Lolcat does Request["I CAN HAZ"] { }
EnglishMan.new.request("yorkshire pudding");
Slovak.new.request("borovicka");
Lolcat.new.request("CHEEZEBURGER");
それは我々が Slovakia で今適切に我々の borovicka を注文することができることを意味する、そしてそれは驚嘆に値する。 あなたがループでそれをして、そして[「非常にひどい」]頭痛を見いだすまで、ロールは、とにかく...オーバーナイトであなた自身の中に入り混ざった。
、パラメータを伴う型に一つのパラメータを提供することに対して、キーワードについてただ構文的な構文糖である. それで:
my Array of Recipe %book;
実際は手段:
my Array[Recipe] %book;
これは、それほど、ネストされることができる:
my Hash of Array of Recipe @library;
ただ存在する:
my Hash[Array[Recipe]] @library;
そのために:
my Array @array;
配列(実際は、 Positional of Array)の配列を意味する。
もし、 T1 が T2 、それから同じくロールより狭いように、あなたが subtyping 関係で2つの型を持っているなら:
role R[::T] { }
role R[::T1, ::T2] { }
R[ T1 ]がR[ T2 ]より狭いように、行動をするだろう。 これは多数のパラメータに及ぶ、しかしながらそれらはまったくもっと狭いあるいは同じこと(これはあなたが1(人・つ)をもっと狭いようにすることができる多数のディスパッチともっと狭いか、あるいはタイされた残りで同じじゃない)に違いない。 すなわち、我々が若干の無関係な型 T3 を持っていると想定して、それでR[ T2 、 T1 ]はR[ T1 、 T1 ]より狭い、しかしRより狭いR[ T2 、 T1 ]ではない[ T1 、 T3 ]。
ネストすることは当然この定義からすると当然起きる、それでロールR[R[ T2 ]]がロールR[R[ T1 ]]より狭い。
もしあなたが sub を持っているなら、このすべては例えば、それを意味する:
sub f(Num @arr) { ... }
それからあなたは Int の配列で同じくそれを呼び出すことができる。
my Int @a = 1,2,3; f(@a);
確かに Perl 6.0.0に関するかぎり、ただコンテナに宣言された型だけが型チェックで重要である。 すなわち、もし我々が sub を持っているなら:
sub f(Int @arr) { ... }
そしての何ででもそれを呼び出せ:
f([1,2,3]); my @a = 1,2,3; f(@a);
それからこれらの呼び出しのいずれも働かないだろう。 型チェックは配列の宣言された型に基づいている、そして内容は型レジ係に知られていない。