2013-03-08 24 views
8

Ben aşağıdaki kodu traverse_ fonksiyonun tipi çıkarsama geliştirmeye çalışıyorum: Yukarıdaki KoşuScala'da kısmen uygulanan türlerin tür çıkarımını geliştirmek mümkün mü?

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) 

verir:

/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 

Belirtmemeyi var, derlemek almak için Ben traverse_ yeniden bir yolu var mı

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

veya: traverse_ argümanlar yazın tür çıkarımı yapmak için, kodun herhangi bir parçası? Türler daha karmaşıklaşmaya başladığında, bu hızlı sinirlenir.

+6

Miles Sabin, Scalaz'da "traverseU" uygulamasında kullanılan bir yolu keşfetti. Tam olarak ne yapmaya çalıştığın gibi geliyor. –

cevap

9

Ben James tarafından işaret edildiği gibi, Miles Sabin'in Unapply trick. Here scalaz repo içinde. Here'straverseU, yardımı ile uygulanmaktadır. Here bazı örnek kullanımlarıdır. Ve burada size bu durumun benim kabataslak (umarım doğru) uygulaması var (not: Ben scalaz tanımlanan, senin ApplicativeApplicativeTest için Applicative müdahale etmemesi yeniden adlandırdık): Ben hala bilmiyorum

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) 

nasıl Bu satırda yaptığım gibi herhangi bir durumda kesinlikle belirtmek yerine Product with Serializable with scala.util.Either[String,Unit] yerine Either[String, Unit] çıkarım: case 5 => Left("x"): Either[String, Unit].

+1

Burada 'Unapply' eyleminin başka bir örneği: http://stackoverflow.com/questions/14924707/how-to-write-a-scalaz-isempty-parameter-for-generic-types –

+0

Mükemmel, teşekkürler! Miles'ın bu şeylerle nasıl çıkacağını asla bilmeyeceğim. – Lachlan

+0

İşte bir tane daha: http://stackoverflow.com/a/16095159/1011414 – mergeconflict

İlgili konular