有時(shí)你必須不擇手段地得到一個(gè)東西。 如果它是一個(gè)文件,你可以使用 Jonathan Stowes 的 URI::FetchFile。 所述模塊檢查四個(gè)模塊中的任何一個(gè)是否可用,并且采用第一個(gè)將 URI 保存為磁盤上的文件。 在他的代碼中有一個(gè)有趣的觸發(fā)了 ENODOC 的行。
$type = try require :: ($class-name);
這里 require
返回一個(gè)類型對象,該類型對象由模塊聲明,并且具有與該模塊相同的名字。
檢查 roast 這個(gè)巧妙的花招并把玩整個(gè)動(dòng)態(tài)模塊的魔法讓我意識(shí)到,我們沒有真正地在文檔中覆蓋這一點(diǎn)。 當(dāng)我嘗試處理一個(gè) ENODOC 時(shí)我喜歡從一個(gè)可編譯的例子開始。 這一次,我們需要兩個(gè)文件。
# M.pm6
unit module M;
class C is export { method m { 'method C::m' } };
class D is export { method m { 'method D::m' } };
# dynamic-modules.p6
use v6;
use lib '.';
subset C where ::('M::C');
my C $context = try {
CATCH { default { .note } };
require ::('M');
::('M::C')
};
dd $context.HOW.^methods.elems;
dd $context.HOW.shortname($context);
通過 require
加載的任何符號在運(yùn)行時(shí)將不可用。因此,我們不能進(jìn)行靜態(tài)類型檢查。使用 subset
和動(dòng)態(tài)查找,我們可以得到一個(gè)類型對象來檢查。 where
從句將與類型對象進(jìn)行智能匹配。由于動(dòng)態(tài)查找很慢,所以像下面這樣緩存類型對象可能是明智的:
subset C where $ //= ::('M::C');
where
會(huì)智能匹配給定的表達(dá)式,除非你用 *
或 $_
手動(dòng)匹配。 給定的表達(dá)式本身不進(jìn)行智能匹配,因此它將被求值。 由于 $
是一個(gè)狀態(tài)變量,它的初始化將只被執(zhí)行一次(對于給定的thunk)。 所以我們最終得到一個(gè)狀態(tài)變量 $
,它填充了第一次使用subset時(shí) ::('M::C')
后面的類型對象。 然后,針對該subset的每個(gè)約束檢查,完成對該類型對象的智能匹配。
//
是 Defined-or 運(yùn)算符。它返回第一個(gè)有定義的的操作數(shù), 否則就返回最后的那個(gè)操作數(shù)。是短路運(yùn)算符。
say Any // 0 // 42; # 0
say Int // Mu // 42; # 42
say Int // Mu // Str; # (Str)
Any
是類型對象(Type Object), 類型對象是未定義的
(unfined)。如果你在類型對象上調(diào)用 .defined
方法, 它會(huì)返回 False
。
你可以用這種方法來找出一個(gè)對象到底是不是類型對象:
my $obj = Int; # Int 是類型對象
if $obj.defined {
say "普通對象, 有定義的對象";
} else {
say "類型對象";
}
而對于普通對象:
0.defined # True
False.defined # True
True.defined # True
"".defined # True
[].defined # True
().defined # True
//=
是多余的,可以被一個(gè)簡單的 =
替換,但它告訴讀者,我期望 $
是未定義的。 我使用定義或賦值
語法,希望讀者熟悉它。
> $a = Int # $a 初始值是未定義的類型對象
(Int)
> $a //= 3 # 等價(jià)于 $a = $a // 3
3
> $a # // 返回第一個(gè)有定義的操作數(shù)
3
現(xiàn)在我們得到一個(gè)類型約束來防止 require
不返回一個(gè)匹配我們期望的名稱的類型。請注意,我們會(huì)檢查名稱,而不是類型或接口。如果您有機(jī)會(huì)設(shè)計(jì)動(dòng)態(tài)加載的模塊,您可能需要定義一個(gè)角色(甚至可能為空),這些角色必須由動(dòng)態(tài)加載的類實(shí)現(xiàn),以確保您可以真正調(diào)用所期望的方法。不只是具有相同名稱的方法。
現(xiàn)在實(shí)際上我們可以按名字加載模塊,并動(dòng)態(tài)解析其中一個(gè)類,并從 try
塊返回它。因?yàn)?M.pm6 定義了一個(gè)模塊(如在 Perl6::Metamodel::ModuleHOW
中)作為它的頂層包,我們不能簡單地接受 require
的返回值,因?yàn)?Module 不是我們在 Perl 6 中最內(nèi)省的東西。請注意,require 所加載的符號可在 try-block 外部通過動(dòng)態(tài)查找獲得。我不知道會(huì)發(fā)生什么如果你 go wild 并加載具有相同完全限定名的符號的模塊??赡軙?huì)有龍。
加載任何一組可能安裝過也可能沒有安裝過的模塊是一種相當(dāng)普遍的情況,對我有限的知識(shí),我們的生態(tài)系統(tǒng)中還沒有那樣一個(gè)模塊。因此,我想挑戰(zhàn)一下,寫一個(gè)運(yùn)行以下接口的模塊。
sub load-any-module(*%module-name-to-adapter);
load-any-module({'Module::Name' => &Callable-adapter});
其中 Callable-adapter
提供了一個(gè)通用接口,將模塊的 sub
或方法調(diào)用轉(zhuǎn)換為用戶代碼需要的任何內(nèi)容。有了這樣的模塊,Jonathan 可以將 URI::FetchFile 減少到 50 行代碼。