Perl 6 - Introspection

Perl 6 支持"泛型, roles 和 多重分發(fā)", 它們都是很好的特點(diǎn), 并且已經(jīng)在其它 advent calendar 中發(fā)布過(guò)了。

但是今天我們要看的是 MOP。 "MOP"代表著元對(duì)象協(xié)議("Meta-Object Protocol")。那意味著, 它們實(shí)際上是你能從用戶(hù)那邊改變的一部分, 而不是對(duì)象、類(lèi)等定義語(yǔ)言的東西。

實(shí)際上, 在 Perl 6中, 你可以為類(lèi)型添加方法, 移除某個(gè)方法, 包裹方法, 使用更多能力增強(qiáng)類(lèi)(OO::ActorsOO::Monitors 就是兩個(gè)這樣的例子), 或者你可以完全重定義它(并且, 例如, 使用 Ruby-like 的對(duì)象系統(tǒng)。這兒有個(gè)例子)。

但是今天, 我們首先看一下第一部分: 自省。在類(lèi)型創(chuàng)建完之后查看它的類(lèi)型, 了解它, 并使用這些信息。

我們將要?jiǎng)?chuàng)建的模塊是基于 Sixcheck 模塊(一個(gè) QuickCheck-like 模塊)的需求: 為某個(gè)類(lèi)型生成一些隨機(jī)數(shù)據(jù), 然后把數(shù)據(jù)喂給我們正測(cè)試的函數(shù), 并檢查某些后置條件(post-condition)。

所以, 我們先寫(xiě)出第一個(gè)版本:

my %special-cases{Mu} = 
  (Int) => -> { (1..50).pick },
  (Str) => -> { ('a'..'z').pick(50).join('') },
;

sub generate-data(Mu:U \t) {
    %special-cases{t} ?? %special-cases{t}() !! t.new;
}

generate-data(Int);

注意以下幾點(diǎn):

  • 我們給 %special-cases 指定了鍵的類(lèi)型。那是因?yàn)槟J(rèn)地, 鍵的類(lèi)型為 Str。顯然地, 我們不想讓我們的類(lèi)型字符串化。我們實(shí)際上做的是指定它們?yōu)?Mu"的子類(lèi)(這在類(lèi)型"食物鏈"的頂端)。
  • 我們?cè)?IntStr 周?chē)派蠄A括號(hào), 以避免字符串化。
  • 我們?cè)诤瘮?shù)參數(shù)類(lèi)型中使用了 :U。那意味著那個(gè)值必須是未定義的(undefined)。類(lèi)型對(duì)象(就像 Int、Str 等等)是未定義的, 所以它能滿(mǎn)足我們(你可能見(jiàn)過(guò)一個(gè)叫 Nil 的不同的未知值)。
  • 類(lèi)型對(duì)象實(shí)際上是對(duì)象, 就像其它任何對(duì)象一樣。這就是為什么我們?cè)陬?lèi)型對(duì)象上調(diào)用 .new方法, 例如, 它和直接調(diào)用 Int.new相同(那對(duì)一致性和 autovivification 很有用)。
  • 我們?yōu)?IntStr 提供了fallback, 因?yàn)檎{(diào)用 Int.newStr.new ( 0 和 "" )不會(huì)在我們創(chuàng)建的數(shù)據(jù)中給我們?nèi)魏坞S機(jī)化。
  • Perl 6 在函數(shù)中自動(dòng)返回最后一個(gè)表達(dá)式。所以不需要在那兒放上一個(gè) return

我們用代碼生成數(shù)據(jù), 公平且公正。但是我們需要生成更多那樣簡(jiǎn)單的數(shù)據(jù)。

我們至少需要支持帶有屬性的類(lèi): 我們想查看屬性列表, 為它們的類(lèi)型生成數(shù)據(jù), 并把它們喂給構(gòu)造器。

我們要能夠看到類(lèi)的內(nèi)部。用 Perl 6 的術(shù)語(yǔ)來(lái)說(shuō), 我們將要到達(dá)的是元對(duì)象協(xié)議(Meta-Object Protocol)。首先我們定義一個(gè)類(lèi):

class Article {
    has Str $.title;
    has Str $.content;
    has Int $.view-count;
}

# 我們可以這樣手動(dòng)創(chuàng)建一個(gè)實(shí)例
Article.new(title      => "Perl 6 Advent, 第 19 天",
            content    => "Magic!",
            view-count => 0
            );

但是我們不想親手創(chuàng)建那個(gè)文章 (article)。我們想把那個(gè) class Article 傳遞給我們的 generate-data 函數(shù), 并返回一個(gè) Article(里面帶有隨機(jī)數(shù)據(jù))。讓我們回到我們的 REPL...

say Article.^attributes;         # (Str $!title Str $!content Int $!view-count)
say Article.^attributes[0].WHAT; # (Attribute)

如果你點(diǎn)擊了 MOP 鏈接, 你不會(huì)對(duì)我們得到一個(gè)含有 3 個(gè)元素的數(shù)組感到驚訝。如果你仍舊對(duì)該語(yǔ)法感到驚訝, 那么 .^是元方法調(diào)用。意思是 a.^b會(huì)被轉(zhuǎn)換為 a.HOW.b(a)

如果我們想知道我們可以訪問(wèn)到什么, 我們問(wèn)它就是了(移除了匿名的那些):

Attribute.^methods.grep(*.name ne '<anon>'); 
# (compose apply_handles get_value set_value 
#      container readonly package inlined WHY set_why Str gist)

Attribute.^attributes # Method 'gist' not found for invocant of class 'BOOTSTRAPATTR'

哎吆… 看起來(lái)這有點(diǎn)太 meta 了。幸好, 我們能使用 Rakudo 的一個(gè)非常好的屬性: 它的大部分都是用 Perl 6寫(xiě)的! 要查看我們可以得到什么, 我們查看源代碼就好了:

# has Str $!name;
...
# has Mu $!type;

我們得到了鍵的名字, 還有去生成值的類(lèi)型。讓我們看看...

> say Article.^attributes.map(*.name)
($!title $!content $!view-count)
> say Article.^attributes.map(*.type)
((Str) (Str) (Int))

天才! 看起來(lái)是正確的。(如果你想知道為什么我們得到 $!(私有的) twigils, 那是因?yàn)?$.只意味著將會(huì)生成的一個(gè) getter 方法)。屬性本身仍然是私有的, 并且在類(lèi)中是可訪問(wèn)的。

現(xiàn)在, 我們唯一要做的事情就是創(chuàng)建一個(gè)循環(huán)...

my %args;

for Article.^attributes -> $attr {
    %args{$attr.name.substr(2)} = generate-data($attr.type);
}
say %args.perl;

這是一個(gè)將會(huì)打印什么的例子:

{:content("muenglhaxrvykfdjzopqbtwisc"), :title("rfpjndgohmasuwkyzebixqtvcl"), :view-count(45)}

每次你運(yùn)行你的代碼你都會(huì)得到不同的結(jié)果(然而我不認(rèn)為它會(huì)創(chuàng)建一篇值得閱讀的文章…)。剩下唯一要做的就是把它們傳遞給 Article 的構(gòu)造函數(shù):

say Article.new(|%args);

(前綴 |允許我們把 %args 作為具名參數(shù)傳遞, 而不是單個(gè)位置參數(shù))。再次, 你應(yīng)該會(huì)打印這些東西:

Article.new(title => "kyvphxqmejtuicrbsnfoldgzaw", content => "jqbtcyovxlngpwikdszfmeuahr", view-count => 26)

呀! 我們?cè)O(shè)法在不了解 Article 的情況下胡亂地(blindly)創(chuàng)建了一個(gè) Article 實(shí)例。 我們的代碼能夠用于為任何期望傳遞它的類(lèi)屬性的構(gòu)造函數(shù)生成數(shù)據(jù)。好了!

PS: 留個(gè)作業(yè)! 移動(dòng)到 generate-data 函數(shù), 以至于我們能給 Article 添加一個(gè) User $.author 屬性, 并且構(gòu)建好這個(gè)函數(shù)。祝你好運(yùn)!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,202評(píng)論 3 426
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 177,742評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,580評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,297評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,688評(píng)論 1 327
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,875評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,438評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,183評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,384評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,612評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 35,022評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 36,297評(píng)論 1 292
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,093評(píng)論 3 397
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,330評(píng)論 2 377

推薦閱讀更多精彩內(nèi)容