2013-03-08 24 views
8

Tôi đang cố gắng để cải thiện những suy luận kiểu của traverse_ chức năng trong các mã dưới đây:Có thể cải thiện suy luận kiểu cho các loại được áp dụng một phần trong Scala không?

import scala.language.higherKinds 

trait Applicative[AF[_]] { 

    def ap[A, B](a: AF[A])(f: AF[A => B]): AF[B] 

    def pure[A](a: A): AF[A] 

    def fmap[A, B](a: AF[A])(f: A => B): AF[B] 

} 

def traverse_[AP[_]: Applicative, A](xs: Iterable[A])(f: A => AP[Unit]): AP[Unit] = { 
    val ap = implicitly[Applicative[AP]] 
    (xs :\ ap.pure(())) { (x, acc) => 
    val apFunc = ap.fmap(f(x))(a => identity[Unit] _) 
    ap.ap(acc)(apFunc) 
    } 
} 

implicit def optionAp = new Applicative[Option] { 

    def ap[A, B](a: Option[A])(f: Option[A => B]): Option[B] = f flatMap (a map _) 

    def pure[A](a: A) = Some(a) 

    def fmap[A, B](a: Option[A])(f: A => B) = a map f 

} 

implicit def eitherAp[L] = new Applicative[({type l[x]=Either[L, x]})#l] { 

    def ap[A, B](a: Either[L, A])(f: Either[L, A => B]): Either[L, B] = f.right flatMap (a.right map _) 

    def pure[A](a: A) = Right(a) 

    def fmap[A, B](a: Either[L, A])(f: A => B) = a.right map f 

} 

// silly, but compiles 
val x = traverse_(1 to 10) { 
    case 5 => None 
    case _ => Some(()) 
} 
println(x) 

// also silly, but does not compile 
val y = traverse_(1 to 10) { 
    case 5 => Left("x") 
    case _ => Right(()) 
} 
println(y) 

Chạy trên cho:

/Users/lodea/tmp/traverse.scala:49: error: no type parameters for method traverse_: (f: Int => AP[Unit])(implicit evidence$1: this.Applicative[AP])AP[Unit] exist so that it can be applied to arguments (Int => Product with Serializable with scala.util.Either[String,Unit]) 
--- because --- 
argument expression's type is not compatible with formal parameter type; 
found : Int => Product with Serializable with scala.util.Either[String,Unit] 
required: Int => ?AP 

val y = traverse_(1 to 10) { 
       ^
/Users/lodea/tmp/traverse.scala:49: error: type mismatch; 
found : Int => Product with Serializable with scala.util.Either[String,Unit] 
required: Int => AP[Unit] 
val y = traverse_(1 to 10) { 
         ^
two errors found 

Để có được nó để biên dịch, tôi phải xác định gõ lập luận để traverse_:

val y = traverse_[({type l[x]=Either[String, x]})#l, Int](1 to 10) { 
    case 5 => Left("x") 
    case _ => Right(()) 
} 

có cách nào tôi có thể cơ cấu lại traverse_, hoặc bất kỳ phần nào khác của mã, để làm cho suy luận kiểu hoạt động? Khi các loại bắt đầu trở nên phức tạp hơn, điều này sẽ gây khó chịu nhanh chóng.

+6

Miles Sabin phát hiện ra một cách để làm điều này, được sử dụng trong việc thực hiện 'traverseU' trong Scalaz. Điều đó nghe có vẻ giống như những gì bạn đang cố gắng làm. –

Trả lời

9

Như được chỉ ra bởi Ben James, bạn đang tìm kiếm số Unapply trick của Miles Sabin. Here nó nằm trong repo scalaz. Here'straverseU, được thực hiện với sự trợ giúp của nó. Here là một số ví dụ về cách sử dụng. Và đây là sơ sài của tôi (hy vọng đúng) thực hiện đối với trường hợp cụ thể của bạn (lưu ý: tôi đã đổi tên của bạn Applicative để ApplicativeTest không can thiệp với Applicative, quy định tại scalaz):

scalaz> core/console 
[warn] Credentials file /home/folone/.ivy2/.credentials does not exist 
[info] Starting scala interpreter... 
[info] 
Welcome to Scala version 2.9.2 (OpenJDK 64-Bit Server VM, Java 1.7.0_15). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

import scalaz._ 

trait ApplicativeTest[AF[_]] { 
    def ap[A, B](a: AF[A])(f: AF[A => B]): AF[B] 
    def pure[A](a: A): AF[A] 
    def fmap[A, B](a: AF[A])(f: A => B): AF[B] 
} 

def traverse_[AP, A](xs: Iterable[A])(f: A => AP)(implicit G: Unapply[ApplicativeTest, AP]): G.M[Unit] = { 
    (xs :\ G.TC.pure(())) { (x, acc) => 
    val apFunc = G.TC.fmap(G(f(x)))(a => identity[Unit] _) 
    G.TC.ap(acc)(apFunc) 
    } 
} 

implicit def optionAp = new ApplicativeTest[Option] { 
    def ap[A, B](a: Option[A])(f: Option[A => B]): Option[B] = f flatMap (a map _) 
    def pure[A](a: A) = Some(a) 
    def fmap[A, B](a: Option[A])(f: A => B) = a map f 
} 

implicit def eitherAp[L]: ApplicativeTest[({type l[x]=Either[L, x]})#l] = 
    new ApplicativeTest[({type l[x]=Either[L, x]})#l] { 
    def ap[A, B](a: Either[L, A])(f: Either[L, A => B]): Either[L, B] = f.right flatMap (a.right map _) 
    def pure[A](a: A) = Right(a) 
    def fmap[A, B](a: Either[L, A])(f: A => B) = a.right map f 
    } 

implicit def iterAp = new ApplicativeTest[Iterable] { 
    def ap[A, B](a: Iterable[A])(f: Iterable[A ⇒ B]): Iterable[B] = f flatMap(a map _) 
    def pure[A](a: A) = Iterable(a) 
    def fmap[A, B](a: Iterable[A])(f: A ⇒ B) = a map f 
} 

// Exiting paste mode, now interpreting. 

import scalaz._ 
defined trait ApplicativeTest 
traverse_: [AP, A](xs: Iterable[A])(f: A => AP)(implicit G: scalaz.Unapply[ApplicativeTest,AP])G.M[Unit] 
optionAp: java.lang.Object with ApplicativeTest[Option]{def pure[A](a: A): Some[A]} 
eitherAp: [L]=> ApplicativeTest[[x]Either[L,x]] 
iterAp: java.lang.Object with ApplicativeTest[Iterable] 

scala> val x = traverse_(1 to 10) { 
    | case 5 => None 
    | case _ => Some(()) 
    | } 
x: Option[Unit] = None 

scala> val y = traverse_(1 to 10) { 
    | case 5 => Left("x"): Either[String, Unit] 
    | case _ => Right(()) 
    | } 
y: Either[String,Unit] = Left(x) 

tôi vẫn không biết làm thế nào để làm cho nó suy ra Either[String, Unit] thay vì Product with Serializable with scala.util.Either[String,Unit] ngoài việc chỉ định đúng loại trong một trong các trường hợp như tôi đã thực hiện trên dòng này: case 5 => Left("x"): Either[String, Unit].

+1

Dưới đây là một ví dụ khác về 'Không thực hiện' trong hành động: http://stackoverflow.com/questions/14924707/how-to-write-a-scalaz-isempty-parameter-for-generic-types –

+0

Hoàn hảo, cảm ơn! Làm thế nào Miles đi kèm với công cụ này tôi sẽ không bao giờ biết. – Lachlan

+0

Đây là một số khác: http://stackoverflow.com/a/16095159/1011414 – mergeconflict