"To achieve enlightenment, our political systems must relinquish their claims on truth, justice and freedom and have to replace them with the search for truth, justice, freedom and reason."
– Immanuel Kant
當我們使用 using 來載入套件的時候,會發現有不少存在在 Base 中的函式是可以使用的。像是 length 可以用來取得陣列的長度,當你載入 DataStructures.jl 時,你同樣可以用 length 來取得 Stack 及 Queue 的長度。
或者是原文中的例子。使用者可以使用 NamedDims.jl 來為你的陣列的維度命名,而你的陣列需要使用 CuArrays.jl 送到 CUDA,這時候你不需要一個可以為 CUDA array 命名的套件來達成這件事。
你會發現在 Julia 語言中似乎不存在套件跟套件之間的差別,或是套件跟標準函式庫之間的區別。這是由於在 Julia 中對於命名空間(namespace)的概念較為薄弱,Julia 各模組之間仍然存在著命名空間,但是 Julia 在 using 時會將這些命名空間去除。這在工程上或許不是一個好的典範,因為會造成命名空間的汙染(namespace pollution)。反過來說,它促使人們相互溝通及協調,來提供更好的套件之間的可組合性。
在其他語言中,常常會告訴你,使用某個套件只需要載入你需要的部份,像是 using Foo: bar, baz,然而 Julia 並不去特別強調這點,using Foo 會載入套件開發者有導出的部份。如果有兩個套件都提供了 predict 來支援他們的模型,這邊借原文的例子再次說明:
1 2 3 4 5 6 7
using Foo using Bar training_data, test_data = ... mbar = BarModel(training_data) mfoo = FooModel(training_data) evaluate(predict(mbar), test_data) evaluate(predict(mfoo), test_data)
julia> plot WARNING: both Gadfly and Plots export"plot"; uses of it inmodule Main must be qualified ERROR: UndefVarError: plot not defined
鴨子定型及多重分派
在原文中提到鴨子定型及多重分派是成就 Julia 成為一個可組合(composable)語言的基石。我個人也認為可組合是 Julia 提供最重要的特性之一,但卻鮮少被人提及。多數人仍然熱衷於語言效能及開發便利性。
鴨子定型的一個很好的比喻是,當一個東西會呱呱叫的時候,那麼他就是鴨子。當一個 bar(x) 可以呼叫時,我不需要去檢查 x 的型別為何,我就直接使用就是了。這樣會構成一種隱性的介面。
當我需要提供一個矩陣相乘的功能時,我會寫以下程式碼:
1 2 3 4 5 6 7 8 9 10 11 12
function multiply(A, B) C = zeros(size(A, 1), size(B, 2)) A = A' for i = 1:size(C, 1) for i = 1:size(C, 2) for k = 1:size(A, 2) C[i, j] = sum(A[:, k] .* B[:, k]) end end end return C end
對於不同的型別需要有不同的行為,這在一般的物件導向語言中稱之為多型。多型在多數物件導向典範中使用的是單一分派(single dispatch),然而 Julia 也支援多型,但是以多重分派(multiple dispatch)的方式支援。因此,廣義而言,Julia 支援物件導向的方式是多重分派,卻不是典型的、語法上的封裝、繼承及(單一分派)多型。
多重分派,可以在需要更細緻行為定義時幫上忙。當 NewArray 支援 sum:
1
sum(::NewArray) = ...
這是單一分派的方式,也可以有多重分派的方式。
1
sum(::NewArray, ::Array) = ...
不過這樣只支援 Array 這個特定型別。或是我們可以乾脆這樣做。
1
sum(::NewArray, ::AbstractArray) = ...
這樣只要是 AbstractArray 的子型別都可以接受。
總結
以上種種的特性成為了可組合性的基石。一個具有可組合性的語言能夠成為各種東西。Julia 的個別的套件都不俱備所有的定義,需要依賴 Julia 語言及標準函式庫的介面及實作。
例如當 Julia 中載入了深度學習框架,那它,連同語言本身,就是一個完整支援深度學習功能的引擎。當同時載入了資料庫與深度學習相關套件,那它就成為了支援深度學習功能的 DBMS。當載入了科學計算及機器學習套件,那它就會變成一個強大的數值計算引擎。以 Julia 的可組合性出發,來打造各式各樣不同的引擎,就像一個單純的編輯器搭配有豐富的外掛(plug-in)一樣。這樣衍生出的生態帶來了各式各樣不同的可能性,也讓套件的 reusability 提升到最高的境界。