Duck type
Duck type 是動態型別語言的一種特性,duck type 物件不需在意其實際的類別,僅需在意該類別是否有提供相對應的公開方法。如以下實例:
class Duck {
method speak {
"Pack pack".say;
}
}
class Dog {
method speak {
"Wow wow".say;
}
}
class Tiger {
method speak {
"Halum halum".say;
}
}
my @animals = (
Duck.new,
Dog.new,
Tiger.new,
);
for @animals -> $a {
$a.speak;
}
在這個例子中,只要 @animals
陣列中的物件有實作 speak
方法即可,我們不需要去檢查各個物件實際的類別。
對於從靜態型別語言轉換過來的程式設計者,時常有檢查物件所屬的類別的衝動;然而,過度地檢查型別,便失去使用動態型別語言的優點。如果想確保型別安全,建議使用 role 來約束類別的行為,如同我們前文所舉的例子。
子類型
子類型 (subtyping) 也是一種實作多型的方法,可以透過繼承或 roles 來實作。由於 Perl 6 是動態型別語言,本身已經支援 duck typing,不太需要使用子類型實作多型。如果想實作子類型,建議使用 role。
函式重載
使用 multi
可以宣告同名但不同參數的函式或方法。我們在先前的範例中已經展示過其用法。
運算子重載
透過運算子重載,衍生類別也可以像內建類別般,使用運算子來操作類別;運算子重載常用在數學相關的類別,像是向量 (vector) 或矩陣 (matrix) 等。以下實例實作向量加法:
role IVector {
method elems { ... }
method at($i) { ... }
}
class Vector does IVector {
has Numeric @!vec;
submethod BUILD(:array(@a)) {
@!vec = @a;
}
method elems {
@!vec.elems;
}
method at($i) {
return @!vec[$i];
}
}
# Overloading indexing method.
multi sub postcircumfix:<[ ]>(IVector $v, $i) {
$v.at($i);
}
# Overloading addition method.
multi sub infix:<+>(IVector $p, IVector $q) {
if $p.elems != $q.elems {
die "Unequal vector length";
}
my @out;
loop (my $i = 0; $i < $p.elems; $i++) {
@out.push($p[$i] + $q[$i])
}
Vector.new(array => @out);
}
my $p = Vector.new(array => (1, 2, 3));
my $q = Vector.new(array => (2, 3, 4));
my $v = $p + $q;
$v[0] == 3 or die "Wrong value";
$v[1] == 5 or die "Wrong value";
$v[2] == 7 or die "Wrong value";
某種程度來說,運算子重載偏向使用者自訂的語法糖,而非必備的語法特性。有許多現代語言支援運算子重載,但也有語言不支援,像是 Java 和 Go。Perl 6 的運算子重載相當靈活,甚至可以自訂新的運算子;但筆者對於自訂運算子的態度較為保守,過度地使用運算子重載,反而會造成代碼難以閱讀。
泛型
由於 Perl 6 是動態型別語言,不太需要使用泛型程式,即可將相同程式碼套用在不同型別上。Perl 6 的 paramaterized role 提供有限度的泛型程式支援,但不若其他語言的泛型系統來得完整,官方對此也著墨甚少,故此處不深入介紹。