2013-02-07 29 views
6

Sử dụng Scala 2.9.1, hãy xem xét hai trường hợp sau đây Either:Tại sao bộ lọc Either.RightProjection # của Scala không trả về một trong hai?

scala> val er: Either[String, Int] = Right(1) 
er: Either[String,Int] = Right(1) 

scala> val el: Either[String, Int] = Left("a") 
el: Either[String,Int] = Left(a) 

Thật tuyệt vời đó, bằng cách sử dụng các leftright dự báo, người ta có thể sử dụng cho-comprehensions (thông qua đơn nguyên thiên vị của Hoặc dự kiến) :

scala> for { r <- er.right } yield r * 2 
res6: Product with Either[String,Int] with Serializable = Right(2) 

scala> for { r <- el.right } yield r * 2 
res7: Product with Either[String,Int] with Serializable = Left(a) 

Ai đó có thể giải thích cho tôi tại sao quyết định không được trả về phương thức filter? Tôi đã có thể dự kiến ​​như sau để làm việc:

scala> for { r <- er.right if r > 2 } yield r * 2 // r is NOT greater than 2! 
res8: Product with Either[String,Int] with Serializable = Left(a) 

Thay vào đó bạn nhận được lỗi sau: : 9: Lỗi: giá trị * không là thành viên của Hoặc [Không có gì, Int] cho {r < - er. đúng nếu r> 2} suất r * 2

Nó xuất hiện rằng cuộc gọi cơ bản trong Either.RightProjection#filter thực sự trả về một Option:

scala> er.right.filter(_ > 2) 
res9: Option[Either[Nothing,Int]] = None 

này đánh bại việc sử dụng các mệnh đề if trong cho-compre lương hưu, ít nhất là cách tôi đang cố gắng sử dụng nó.

Có ai có giải thích tại sao thiết kế này là như vậy không?

Trả lời

11

Nó ghi lại thực tế là nếu bạn có Right(b), nhưng vị từ bộ lọc của bạn không thành công, bạn không có giá trị để đặt trong một Left.

Bạn có thể tưởng tượng triển khai hoạt động cho trường hợp của mình là Either[String, Int], bằng cách không có giá trị mặc định là Left(""). Thư viện chuẩn Scala không có cơ sở để tạo ra một giá trị cho bạn, bởi vì nó không bao gồm một khái niệm như một monoid mà sẽ xác định giá trị "rỗng" cho một kiểu.

Thư viện Scalaz không bao gồm một typeclass monoid, và phiên bản 7 cũng bao gồm một loại phải thiên vị phân ly, \/[A, B] (đẳng cấu với Either[A, B]) trong đó có một phương pháp filter iff loại trái là một monoid:

scala> \/.right[String, Int](1).filter(_ > 2) 
res1: scalaz.\/[String,Int] = -\/() 

Nhưng bạn không thể làm điều này cho trường hợp chung - nếu bạn có một Either[Nothing, Int], bạn không bao giờ có thể tạo ra một giá trị trái.

+1

Thật là một câu trả lời tuyệt vời, có ý nghĩa tổng thể. Điều đó có nghĩa là tôi nên làm một cái gì đó như: cho {r <- er.right.filter (_> 2) .toRight ("a"). JoinRight.right} yield r * 2 –

+1

Bạn có thể thích một mẫu phù hợp: 'er khớp với {case Right (b) nếu b> 2 => Right (b * 2); case _ => Left ("a")} ' –

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