擴(kuò)展 Perl 6 中的類型
使用繼承
class BetterInt is Int {
method even { self %% 2 }
}
my BetterInt $x .= new: 42;
say $x.even;
$x .= new: 71;
say $x.even;
say $x + 42;
# OUTPUT:
# True
# False
# 113
my BetterInt $x
約束 $x
只能包含 BetterInt 或它的子類這種類型的對(duì)象肪笋。.= new: 42
等價(jià)于 = BetterInt.new: 42
碎绎。
下面的子例程期望接收一個(gè) Int 型的參數(shù)鸦难,但是你給它傳遞一個(gè) BetterInt 類型的參數(shù)它會(huì)很高興:
sub foo(Int $x) { say "\$x is $x"}
my BetterInt $x .= new: 42;
foo $x;
# OUTPUT:
# $x is 42
But... But... But...
另外一個(gè)選擇是摻合進(jìn)一個(gè)角色(role)珍语。but 中綴操作符創(chuàng)建對(duì)象的一份拷貝并為該對(duì)象添加一個(gè)方法:
my $x = 42 but role { method even { self %% 2 } };
say $x.even;
# OUTPUT:
# True
當(dāng)然角色不一定是內(nèi)聯(lián)的强衡。這兒有另外一個(gè)例子使用了一個(gè)預(yù)定義的角色并且還展示了我們的對(duì)象確實(shí)被拷貝了一份:
role Better {
method better { "Yes, I am better" }
}
class Foo {
has $.attr is rw
}
my $original = Foo.new: :attr<original>;
my $copy = $original but Better;
$copy.attr = 'copy';
say $original.attr; # still 'original'
say $copy.attr; # this one is 'copy'
say $copy.better;
say $original.better; # fatal error: can't find method
# OUTPUT:
# original
# copy
# Yes, I am better
# Method 'better' not found for invocant of class 'Foo'
# in block <unit> at test.p6 line 18
這看起來挺不錯(cuò)的,但是對(duì)于我們?cè)瓉淼哪繕?biāo)來說合呐,這個(gè)方法還是相當(dāng)弱的:
my $x = 42 but role { method even { self %% 2 } };
say $x.even; # True
$x = 72;
say $x.even; # No such method!
那個(gè)角色被混合進(jìn)我們?nèi)萜骼锩娲鎯?chǔ)的對(duì)象中了暮的;所以一旦我們?cè)谌萜髦蟹胚M(jìn)了一個(gè)新的值,或高級(jí)點(diǎn)的東西淌实,那么 .even 方法就不見了冻辩,除非我們?cè)俅伟涯莻€(gè)角色混合進(jìn)來猖腕。
子例程
你知道你可以把子例程當(dāng)做方法用嘛? 你接收那個(gè)對(duì)象作為子例程的第一個(gè)位置參數(shù)并且你甚至能繼續(xù)使用鏈?zhǔn)椒椒ㄕ{(diào)用恨闪,但是不能把那些鏈子分解成多行:
sub even { $^a %% 2 };
say 42.&even.uc;
# OUTPUT:
# TRUE
這確實(shí)是為核心類型添加額外功能的一種得體方式倘感。我們的子例程定義中的 $^a
引用第一個(gè)參數(shù)(我們?cè)谡{(diào)用的那個(gè)對(duì)象)并且整個(gè)子例程也可以被寫為:
sub ($x) { $x %% 2 }
飛龍?jiān)谔?/h2>
不管Javaccript 的那些人們?cè)趺锤阏f, 然而擴(kuò)充原生類型是危險(xiǎn)的。因?yàn)槟阏绊懗绦虻乃胁糠至省I踔量床坏侥愕臄U(kuò)充的模塊也受到影響侠仇。
現(xiàn)在我有權(quán)告訴你,我跟你說過犁珠,你工作的核電廠融化了,讓我們看看一些代碼:
# Foo.pm6
unit module Foo;
sub fob is export {
say 42.even;
}
# Bar.pm6
unit module Bar;
use MONKEY-TYPING;
augment class Int {
method even { self %% 2 }
}
# test.p6
use Foo;
use Bar;
say 72.even;
fob;
# OUTPUT:
# True
# True
所有的行為都發(fā)生在 Bar.pm6 中互亮。首先犁享,我們寫了一行 use MONKEY-TYPING 聲明,它告訴我們正在做一些危險(xiǎn)的行為豹休。然后我們?cè)陬?class Int 的前面使用了 augment 關(guān)鍵字以擴(kuò)充這個(gè)已經(jīng)存在的類炊昆。我們的擴(kuò)充添加了一個(gè)叫 even 的方法以告訴我們那個(gè) Int 是否是偶數(shù)。
所有的整數(shù)都可以使用 even 方法了威根,這雖然達(dá)到了我們的要求但是有點(diǎn)危險(xiǎn)凤巨。
我邪惡了
我們來擴(kuò)充 Cool 類型以涵蓋所有的西文排版行長(zhǎng)單位:
use MONKEY-TYPING;
augment class Cool {
method even { self %% 2 }
}
.say for 72.even, '72'.even, pi.even, ?.even;
# OUTPUT:
# Method 'even' not found for invocant of class 'Int'
# in block <unit> at test.p6 line 8
糟糕,程序奔潰了洛搀!原因是在我們擴(kuò)充 Cool 類型的時(shí)候敢茁,派生自 Cool 的所有類型已經(jīng)成型了(composed)。所以為了讓它能工作留美,我們必須使用 .^compose
元對(duì)象協(xié)議方法來重新構(gòu)成它們:
use MONKEY-TYPING;
augment class Cool {
method even { self %% 2 }
}
.^compose for Int, Num, Rat, Str, IntStr, NumStr, RatStr;
.say for 72.even, '72'.even, pi.even, ?.even;
# OUTPUT:
# True
# True
# False
# False
它現(xiàn)在能工作了彰檬!Int, Num, Rat, Str, IntStr, NumStr, RatStr 類型擁有了 .even
方法(注意:這些不是繼承自 Cool 的僅有的類型)! 這既邪惡又讓人吃驚。
結(jié)論
當(dāng)擴(kuò)充 Perl 6 的核心類型或其它任意類的功能時(shí)谎砾,你有幾種選擇逢倍。
- 使用 is Class 子類
- 使用 but Role 混合一個(gè)角色
- 使用
$objec.&sub
調(diào)用子例程作為方法使用 - 使用 augment(注意安全)