2013-05-02 22 views
7

Định nghĩa của tùy chọn monoid của scalaz là như sau:Tại sao việc thực hiện Monoid của Option cho phép đánh giá hàm f2 hai lần?

implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = new Monoid[Option[A]] { 
    def append(f1: Option[A], f2: => Option[A]) = (f1, f2) match { 
    case (Some(a1), Some(a2)) => Some(Semigroup[A].append(a1, a2)) 
    case (Some(a1), None)  => f1 
    case (None, Some(a2))  => f2 
    case (None, None)   => None 
    } 

    def zero: Option[A] = None 
} 

f2 là một đường chuyền theo tên param có nghĩa là mỗi cuộc gọi sẽ đánh giá sự biểu hiện. Tại sao nó lại được đánh giá lại khi nó vừa được đánh giá trong khớp mẫu? Trả lại Some(a2) phải là kết quả tương tự và biểu thức f2 có thể rất tốn kém.

Tôi có thiếu gì đó không?

Scalaz's Option.scala source

+3

Có lẽ một sự chuyển giao từ suy nghĩ của Haskell có định nghĩa tương đương rẻ? –

+0

Bạn đã thử nghiệm điều này? Đặt một số println bên trong nó và kiểm tra. – Felix

+0

Có, tôi đã kiểm tra nó và println bị nhấn hai lần. Tôi sẽ sửa chữa nó và gửi một yêu cầu kéo đến scalaz tôi đoán. – coltfred

Trả lời

4

Có vẻ với tôi như nó đã được viết để làm nổi bật tính đối xứng của vấn đề và cho rõ ràng, không phải cho tốc độ. Bạn không thể chỉ bỏ sự lười biếng của đối số thứ hai kể từ khi Semigroup định nghĩa nó theo cách đó, và trong các ngữ cảnh khác khi sự lười biếng của đối số thứ hai có thể là cần thiết. Để duy trì sự thể hiện trực quan về tính đối xứng của vấn đề, bạn có thể chỉ cần thêm

val g2 = f2 // Force evaluation 
(f1, g2) match { ... 

hoặc somesuch.

(Sẽ thật tuyệt nếu đối số tên có thể được gọi là lười biếng để tự động ghi nhớ chúng.)

+1

Tôi đã gửi nó dưới dạng yêu cầu kéo và họ đã hợp nhất nó, nhưng giải pháp của bạn cũng sẽ hoạt động. https://github.com/scalaz/scalaz/pull/352 – coltfred

Các vấn đề liên quan