Nic Lin's Blog

喜歡在地上滾的工程師

Rails 的 scope 為什麼用 lambda? Proc 與 lambda 不同之處

lambda 和 Proc 幾乎一模一樣,主要差異,差在「檢查參數」和「丟回控制權」

打開 irb console 來試試看下面兩個 method

def test_proc
  test = Proc.new { return "Return Successful" }
  test.call

  "Proc not return"
end

def test_lambda
  test = lambda { return "Return Successful" }
  test.call

  "Lambda not return"
end

我們會發現 lambda 會將控制權丟回呼叫方,繼續執行該方法,而 proc 的 return 則不會,是立即跳出。

test_proc
 => "Return Successful"

test_lambda
 => "Lambda not return"

再來我們嘗試看看檢查參數的差異,分別執行下面兩段程式碼

lambda { |name| puts "Hi, #{name}"}.call
=> ArgumentError (wrong number of arguments (given 0, expected 1))

Proc.new { |name| puts "Hi, #{name}"}.call
=> Hi,
nil

這裡也可以發現 lambda 會對參數進行檢查,如果沒有帶入則會直接拋出 error,但 proc 會直接以 nil 帶入參數。

這也就是為什麼 Rails 中的 ActiveRecord model 在使用 scope 時,會用 lambda 進行傳遞,原因是相比 proc 來說,更為謹慎

假設我們寫會帶入參數的 scope

scope :with_type, ->(type) { where(type: type) }

你不會希望沒傳值進去他突然變 nil

這樣會有難以 Debug 的情況,假設用 proc 來做的話

當你明確定義參數時,在不傳入的情況下直接呼叫該 scope Product.with_type

結果 SQL query 依舊能夠執行,不會噴錯,因為 proc 會將沒帶入的參數直接設為 nil,在 SQL query 就等同於執行where(type: nil)

我們定義期望參數傳入,在這邊如果不傳入參數,SQL 卻還是能夠進行搜尋,接著就會出現你沒預料到的狀況,這樣是不是有些隱諱?

反而用 lambda 能夠確保參數確實傳遞,如果沒有傳遞,直接拋出 error 告訴你不能這樣做,避免不必要的隱諱查詢。

comments powered by Disqus