Ruby 開發者使用 Crystal 的五大理由
這是一篇由 Mark Siemers 撰寫的客座文章,他自願提供一系列 Crystal 部落格文章的建議。請期待更多內容,或者 - 更好的是 - 聯絡我們 撰寫您自己的文章。
1. 極低的學習曲線
想想過去 5-10 年來流行的程式語言。你會想到什麼?Elixir、Go、Rust 嗎?它們在效能上都比 Ruby 有優勢,但學習和掌握起來更困難。
如果能夠以更簡單的學習曲線獲得效能提升呢?
有多簡單?我們來看看一些程式碼。
問: 以下哪個模組是用 Ruby 寫的?哪個是用 Crystal 寫的?
module Year
def self.leap?(year)
year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
end
end
module Hamming
def self.distance(a,b)
a.chars.zip(b.chars).count{|first, second| first != second }
end
end
答: 這是個陷阱題 - 兩者都是。上面的模組可以在 Ruby 或 Crystal 中執行。是不是很酷?
更多程式碼相似的範例請見這裡。
這並不表示所有 Ruby 程式碼都能在 Crystal 中執行(反之亦然),但您可以在 Crystal 中立即完成許多工作,並且在第一天,甚至第一分鐘就具有生產力。
Crystal(一種強型別和編譯語言)如何表現得像 Ruby(一種動態和鴨子型別語言)?Crystal 的編譯器結合了兩種強大的技術:型別推斷和 聯合型別。這讓編譯器能夠讀取您的類 Ruby 程式碼並找出(推斷)要使用的正確型別。
除了相似之處,Crystal 還提供了一些比 Ruby 更核心的優勢。例如...
2. 編譯時檢查和方法多載
當您在 Ruby 中寫 is_a?
或 respond_to?
來確保程式碼不會中斷時,是否覺得很 hacky?您是否曾經擔心所有您沒有放入這些檢查的地方?這些都是等待發生的錯誤嗎?
Crystal 是一種編譯語言,會在編譯時檢查您所有的方法輸入和輸出。如果任何型別不一致,它們會在執行時之前被捕獲。
讓我們重新看看上面的 Year::leap?
範例。在 Ruby 中,當輸入不是整數時會發生什麼事?
Year.leap?("2016") #=> false
Year.leap?(Date.new(2016, 1, 1)) #=> undefined method `%' for #<Date: 2016-01-01 ... >
對於 String
我們會得到錯誤的答案,對於 Date
我們會得到執行時例外。在 Ruby 中修復這些問題至少需要一個 is_a?
陳述式
module Year
def self.leap?(input)
if input.is_a? Integer
input % 400 == 0 || (input % 100 != 0 && input % 4 == 0)
elsif input.is_a? Date
input.leap?
else
raise ArgumentError.new("must pass an Integer or Date.")
end
end
end
您覺得這個方法怎麼樣?我們仍然有可能發生執行時例外,只是錯誤訊息更有幫助。
在 Crystal 中,我們可以選擇明確地輸入我們的輸入(和輸出)。我們可以將方法簽章變更為 self.leap?(year : Int)
,並保證輸入的是整數。
我們會在編譯時而不是執行時收到有用的訊息
Year.leap?("2016")
Error in line 10: no overload matches 'Year.leap?' with type String
Overloads are:
- Year.leap?(year : Int)
如果我們想在我們的模組中新增對 Time
的支援(想想 Ruby 中的 DateTime
),我們可以多載 Year::leap?
module Year
def self.leap?(year : Int)
year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
end
def self.leap?(time : Time)
self.leap?(time.year)
end
end
與 Ruby 一樣,方法多載允許輸入的靈活性,但沒有鴨子型別的猜測。編譯時檢查可以防止型別不符錯誤進入生產環境。
說到生產環境,那麼...
3. 極快的效能
編譯的另一個優點是速度和最佳化。在比較 Ruby 和 Crystal 的效能時,通常可以用數量級而不是百分比來表示。
在一個範例中,在 Crystal 中加總隨機數字的速度可能比 Ruby 快 10 個數量級(快約 3,700 萬 %)。這是由於編譯器最佳化和在 Crystal 中使用基本資料型別的能力。這確實存在大數整數溢出的風險(請參閱 Ary 的解釋)。
Crystal 的內建 HTTP 伺服器在基準測試中能夠處理超過每秒 200 萬個請求。許多 Web 框架也持續為 Web 應用程式提供毫秒級以下的響應時間。
這引導我們到下一個重點...
4. 您想要的 Web 框架已經在這裡
喜歡 Rails(甚至 Elixir 的 Phoenix)的完整性嗎?您會在 Amber 框架中感到賓至如歸。
Sinatra 的簡潔性和易於自訂是否更符合您的風格?您會發現 Kemal 的簡潔性。
您想要一個利用編譯時檢查來進行強型別參數、HTTP 動詞和資料庫查詢的全端框架嗎?今天是您幸運的一天。
在 1 月期間,這些 Web 框架中的每一個都將在自己的專屬文章中重點介紹。請回來查看 Crystal 部落格以瞭解更多資訊。
5. Crystal 是用 Crystal 寫的!它很容易理解,並且可以為該語言做出貢獻
您讀過 Ruby 的原始碼嗎?嘗試找出一些 Enumerable
方法是如何運作的嗎?
static VALUE
enum_all(int argc, VALUE *argv, VALUE obj)
{
struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
return memo->v1;
}
要弄清楚該程式碼在做什麼需要多長時間?如果您從未接觸過 C 程式碼,可能需要很長的時間。
將其與 Crystal 的 Enumerable#all?
實作進行比較
def all?
each { |e| return false unless yield e }
true
end
弄清楚這一點需要多長時間?如果您了解 Ruby 或 Crystal,可能只需要幾秒鐘。
考慮到這一點,請思考 98.4% 的 Crystal 是用 Crystal 寫的,只有 0.3% 是用 C++ 寫的。
Ruby 有 30.6% 是用 C 寫的,64.8% 是用 Ruby 寫的。
事實上,Crystal 中只有一個檔案是用 C++ 寫的,所以只要您沒有變更 LLVM 擴充功能,您在 Crystal 語言中尋找的任何東西幾乎都可以保證是用 Crystal 寫的。
讀取、理解和貢獻 Crystal 比您能找到的任何語言都容易。
從哪裡開始?
如果您想試試看 Crystal 程式語言,以下是一些入門的好資源
- Crystal Play - 一個用於 Crystal 的線上 REPL 類工具
- 安裝 Crystal
- 適用於 Ruby 開發者的 Crystal
- Crystal Exercisms
- 在幾分鐘內建立您自己的 HTTP 伺服器