2014-07-21 18 views
9

Tôi không chắc chắn làm thế nào để mô tả vấn đề này, vì vậy tôi sẽ chỉ hiển thị các chữ ký loại.Có cách nào để làm phẳng các monads lồng nhau của các loại khác nhau không?

Tôi có một thể hiện các nội dung sau:

val x:Future[F[Future[F[B]]]] = ??? 

Và tôi muốn một thể hiện của:

val y:Future[F[B]] = ??? 

F là một đơn nguyên, vì vậy tôi có các phương pháp sau:

def pure[A](a:A):F[A] = ??? 
def flatMap[A, B](fa:F[A], f:A => F[B]):F[B] = ??? 
def map[A, B](fa:F[A], f:A => B):F[B] = flatMap(fa, (a:A) => pure(f(a))) 

Tôi nghĩ những điều sau đây sẽ hiệu quả nhưng không cảm thấy đúng:

x.flatMap { fWithFuture => 
    val p = Promise[F[B]] 
    flatMap(fWithFuture, (futureF: Future[F[B]]) => { 
    p.completeWith(futureF) 
    pure(()) 
    }) 
    p.future 
} 

Có khái niệm nào bị thiếu không?

Một chút thông tin cơ bản. Tôi đang cố xác định một chức năng như sau:

def flatMap[A, B](fa:Future[F[A]], f: A => Future[F[B]]):Future[F[B]] = ??? 

Có thể đây là một điều kỳ lạ về mặt khái niệm. Bất kỳ lời khuyên về abstractions hữu ích được chào đón.

+1

FWIW, tôi đã giải quyết vấn đề này trong mã trước khi sử dụng phương pháp tiếp cận vũ lực mà bạn có ở đây. Tôi cũng không cảm thấy đúng khi làm điều đó, nhưng tôi đã không tìm ra điều gì tốt hơn. – joescii

+2

Chào mừng đến với thế giới vụng về và vụng về của máy biến áp đơn nguyên! –

+0

@RexKerr là bạn nói đây là cách để làm điều đó? – EECOLOR

Trả lời

2

Điều này có thể trả lời phần "Và tôi muốn một bản sao của:".

$ scala 
Welcome to Scala version 2.10.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_05). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> import scala.concurrent.Future 
import scala.concurrent.Future 

scala> import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.ExecutionContext.Implicits.global 

scala> Future(List(Future(1),Future(2),Future(3))) // Future[F[Future[B]]] 
res0: scala.concurrent.Future[List[scala.concurrent.Future[Int]]] = [email protected] 

scala> res0.map(Future.sequence(_)) // transformed to Future[Future[F[B]] 
res1: scala.concurrent.Future[scala.concurrent.Future[List[Int]]] = [email protected] 

scala> res1.flatMap(identity) // reduced to Future[F[B]] 
res2: scala.concurrent.Future[List[Int]] = [email protected] 

Hy vọng rằng định nghĩa flatMap dưới đây sẽ cung cấp cho một ý tưởng về chuyển các loại :) tôi thay thế F với một loại List để dễ hiểu.

scala> def flatMap[A, B](fa:Future[List[A]], f: A => Future[List[B]]):Future[List[B]] = { 
    |  val x: Future[List[Future[List[B]]]] = fa.map(_.map(f)) 
    |  val y: Future[Future[List[List[B]]]] = x.map(Future.sequence(_)) 
    |  val z: Future[Future[List[B]]] = y.map(_.map(_.flatten)) 
    |  z.flatMap(identity) 
    | } 
flatMap: [A, B](fa: scala.concurrent.Future[List[A]], f: A => scala.concurrent.Future[List[B]])scala.concurrent.Future[List[B]] 
+0

'chuỗi' đòi hỏi một' TraversableOnce' (trong trường hợp của bạn là một 'Danh sách'), do đó, nó sẽ không hoạt động đối với các loại khác –

+0

Về bản chất' trình tự' làm điều tương tự. Nó chỉ sử dụng 'CanBuildFrom' để ẩn đột biến. – EECOLOR

6

Như Rex Kerr lưu ý ở trên, bạn thường có thể sử dụng máy biến áp đơn lẻ để xử lý tình huống mà bạn thấy mình xen kẽ các lớp như thế này. Ví dụ, nếu F đây là Option, bạn có thể sử dụng OptionT đơn nguyên biến Scalaz 7.1 's viết của bạn flatMap:

import scalaz._, Scalaz._ 

type F[A] = Option[A] 

def flatMap[A, B](fa: Future[F[A]], f: A => Future[F[B]]): Future[F[B]] = 
    OptionT(fa).flatMap(f andThen OptionT.apply).run 

OptionT[Future, A] đây là một loại wrapper cho Future[Option[A]]. Nếu F của bạn là List, chỉ cần thay thế OptionT bằng ListTrun với underlying (v.v.).

Điều thú vị là khi bạn đang làm việc với OptionT[Future, A], ví dụ, bạn thường có thể tránh kết thúc bằng số Future[Option[Future[Option[A]]]] ngay từ đầu — xem câu trả lời của tôi here để biết một số cuộc thảo luận chi tiết hơn.

Một nhược điểm là không phải tất cả các đơn vị đều có máy biến áp. Ví dụ: bạn có thể đặt Future ở cuối ngăn xếp (như tôi đã thực hiện ở trên), nhưng không thực sự là cách hữu ích để xác định FutureT.

+0

"nhưng không thực sự là một cách hữu ích để xác định' FutureT' "Tại sao vậy? Đó thực sự là những gì tôi cần. – EECOLOR

+0

Đó là [không thể xác định mà không bị chặn] (https://github.com/typelevel/scalaz-contrib/issues/10). Khi bạn đang làm việc với 'Future [Option [A]]' biến áp đơn nguyên tương ứng là 'OptionT [Future, A]', không phải 'FutureT [Option, A]' - bạn có chắc bạn cần 'FutureT' không? –

+0

Tôi không chắc chắn nếu tôi cần một 'FutureT'. Điều duy nhất tôi biết chắc chắn là tôi biết tôi có một 'Tương lai' và một số đơn nguyên tùy ý bên trong nó ... Tôi sẽ quay lại đoạn mã để xem liệu tôi có thể cấu trúc nó theo cách khác cho phép tôi bỏ qua 'FutureT'. – EECOLOR

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