2013-03-01 30 views
8

Tôi có một danh sách các id chuỗi biểu diễn các bản ghi DB. Tôi muốn tải chúng từ DB một cách không đồng bộ, sau đó tải từng bản ghi lên máy chủ từ xa một cách không đồng bộ, sau đó khi tất cả được tải lên xong, hãy ghi lại các id của các bản ghi đã được tải lên.TraversableOnce, Future và Option trong Scala for comprehension

Vì tôi đang sử dụng Scala 2.9.2, tôi đang sử dụng triển khai Core-util của Twitter trong tương lai, nhưng nó sẽ hoạt động giống như 2.10 tương lai về biến đổi Monadic.

Khái niệm chung là thế này:

def fetch(id: String): Future[Option[Record]] 
def upload(record: Record): Future[String] 
def notifyUploaded(ids: Seq[String]): Unit 

val ids: Seq[String] = .... 

Tôi đang cố gắng để làm điều này thông qua một cho sự hiểu biết nhưng thực tế là lấy lợi nhuận một Tương lai của Tùy chọn làm cho nó tối nghĩa và mã không biên dịch:

for { 
    id <- ids 
    maybeRecord <- fetch(id) 
    record <- maybeRecord 
    uploadedId <- upload(record) 
} yield uploadedId 

Biên dịch này kết quả trong các lỗi sau:

scala: type mismatch; 
found : com.twitter.util.Future[String] 
required: Option[?] 
    uploadedId <- upload(record) 
       ^

tôi mi gì ssing? tại sao trình biên dịch mong đợi uploadId là một Option? là có cách nào đẹp, tôi có thể làm việc xung quanh này?

+0

thể trùng lặp của [Type Mismatch trên Scala Đối Hiểu] (http://stackoverflow.com/questions/4719592/type-mismatch-on-scala-for-comprehension) –

+1

Một đơn nguyên là một monoid trong danh mục ** Endo ** - functors. Chỉ cần nói ' – folone

+1

@folone: ​​Tôi sợ rằng không phải ai cũng sẽ có được [trò đùa] (http://stackoverflow.com/questions/3870088/a-monad-is-just-a-monoid-in-the-category-of -endofunctors-whats-the-problem). Chỉ cần nói ' –

Trả lời

6

xem xét chữ ký của flatMap (hoặc ràng buộc) chức năng:

trait Monad[M[_]] { 
    def flatMap[A](a : M[A], f : A => M[B]) : M[B] 
    .... 

Trong trường hợp của bạn, bạn đang cố gắng sử dụng flatMap trên một Option, cho nó một f mà tạo ra một Future. Nhưng như trong chữ ký ở trên, f nên tạo ra một cái gì đó trong cùng một monad mà nó được gọi là trên.

Scala không nhất thiết phải hữu ích về mặt này, vì nó khá tốt khi chuyển đổi mọi thứ (ví dụ: Seq s) để bạn có ấn tượng rằng bạn có thể chuỗi các cuộc gọi flatMap tùy ý, bất kể của container.

Điều bạn có thể muốn là 'Biến áp đơn lẻ', điều này mang lại cho bạn một số khả năng sáng tác monads. Debasish Ghosh có một bài viết về sử dụng máy biến áp Scalaz monad here.

0

Bạn không thể kết hợp tất cả các loại khác nhau trong một để hiểu, tôi đã tìm ra rằng bạn có thể trộn Seq và Option và kết quả sẽ là Seq hoặc Option tùy thuộc vào đầu tiên. Không thể trộn Tương lai và Seq hoặc Tùy chọn. Nếu bạn muốn sử dụng cho hiểu, bạn sẽ phải cascade chúng ít. Trong những trường hợp như vậy, nó có thể đẹp hơn với bản đồ/flatMap. Tôi đã thực hiện câu hỏi của bạn theo cả hai cách và thêm các loại vào một vài kết quả trung gian để bạn thấy sự lộn xộn đang được tạo ra trong khi làm việc với tất cả các loại khác nhau.

object TestClass { 

    import scala.concurrent.Future 
    import scala.concurrent.ExecutionContext.Implicits.global 
    import scala.concurrent._ 
    import scala.concurrent.duration._ 

    case class Record(id: String) 


    def fetch(id: String): Future[Option[Record]] = Future { 
    Thread.sleep(1000); 
    Some(Record(id)) 
    } 

    def upload(record: Record): Future[String] = Future { 
    Thread.sleep(3000); 
    record.id + "_uploaded" 
    } 

    def notifyUploaded(ids: Seq[String]): Unit = println("notified" + ids) 

    val ids: Seq[String] = Seq("a", "b", "c") 

    def main(args: Array[String]): Unit = { 
    forComprehensionImpl() 
    mapAndFlatMapImpl() 
    } 

    def forComprehensionImpl() = { 
    val result: Seq[Future[Option[Future[String]]]] = for { 
     id <- ids 
    } yield { 
     for { 
     maybeRecord <- fetch(id) 
     } yield { 
     for { 
      record <- maybeRecord 
     } yield { 
      for { 
      uploadedId <- upload(record) 
      } yield uploadedId 
     } 
     } 
    } 
    val result2: Future[Seq[Option[Future[String]]]] = Future.sequence(result) 
    val result3: Future[Unit] = result2.flatMap { x: Seq[Option[Future[String]]] => 
     Future.sequence(x.flatten).map(notifyUploaded) 
    } 
    Await.result(result3, Duration.Inf) 
    } 


    def mapAndFlatMapImpl() = { 
    val res: Seq[Future[Iterable[String]]] = ids.map { id => 
     fetch(id).flatMap { maybeRecord => 
     val res1: Option[Future[Seq[String]]] = maybeRecord.map { record => 
      upload(record) map (Seq(_)) 
     } 
     res1 match { 
      case Some(a) => a 
      case None => Future(Seq()) 
     } 
     } 
    } 
    val res3: Future[Unit] = Future.sequence(res) map (a => notifyUploaded(a.flatten)) 
    Await.result(res3, Duration.Inf) 
    } 
} 
Các vấn đề liên quan