2013-03-08 9 views
8

मैं नीचे दिए गए कोड में traverse_ समारोह के प्रकार निष्कर्ष सुधार करने के लिए कोशिश कर रहा हूँ:स्कैला में आंशिक रूप से लागू प्रकारों के लिए प्रकार अनुमान को बेहतर बनाना संभव है?

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) 

ऊपर चल रहा है देता है:

/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 

यह संकलित करने के लिए प्राप्त करने के लिए, मैं स्पष्ट करना पड़ता traverse_ तर्क टाइप करें:

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

वहाँ एक रास्ता मैं traverse_ गठित कर सकता है, या टाइप अनुमान बनाने के लिए कोड का कोई अन्य भाग? जब प्रकार अधिक जटिल हो जाते हैं, तो यह कष्टप्रद तेज़ हो जाता है।

+6

माइल्स सबिन ने ऐसा करने का एक तरीका खोजा, जिसका उपयोग स्कालज़ में 'ट्रैवर्सयू 'के कार्यान्वयन में किया जाता है। यह वैसे ही लगता है जो आप करने की कोशिश कर रहे हैं। –

उत्तर

9

जैसा कि बेन जेम्स ने बताया है, आप माइल्स सबिन के Unapply trick की तलाश में हैं। Here यह स्केलज़ रेपो में है। Here'straverseU, इसकी मदद से लागू किया गया। Here कुछ उदाहरण उपयोग हैं। और यहाँ मेरा अधूरा (उम्मीद सही) निवारण हेतु कार्यान्वयन है (ध्यान दें: मैं अपने ApplicativeApplicativeTest को Applicative साथ हस्तक्षेप न करने का नया नाम दिया गया है, 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) 

मैं अभी भी पता नहीं कैसे करने के लिए Product with Serializable with scala.util.Either[String,Unit] के बजाय Either[String, Unit] को इस लाइन पर किए गए मामलों में से एक में सख्ती से निर्दिष्ट प्रकार के अलावा इसे करें।

+1

यहाँ 'कार्रवाई में Unapply' का एक और उदाहरण है: http://stackoverflow.com/questions/14924707/how-to-write-a-scalaz-isempty-parameter-for-generic-types –

+0

बिल्कुल सही है, धन्यवाद! इस सामग्री के साथ माइल्स कैसे आता है मैं कभी नहीं जानता। – Lachlan

+0

यहां एक और है: http://stackoverflow.com/a/16095159/1011414 – mergeconflict

संबंधित मुद्दे