2013-03-08 10 views
8

Ich versuche, die Typinferenz der traverse_ Funktion in dem folgenden Code zu verbessern:Ist es möglich, die Typinferenz für teilweise angewandte Typen in Scala zu verbessern?

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) 

die oben Laufen gibt:

/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 

es zu kompilieren, ich habe die angeben geben Sie Argumente traverse_:

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

gibt es eine Möglichkeit, ich traverse_ restrukturieren kann, oder Irgendein anderer Teil des Codes, um die Typinferenz zu arbeiten? Wenn die Typen immer komplexer werden, wird das schnell nervig.

+6

Miles Sabin hat einen Weg gefunden, dies zu tun, der bei der Implementierung von 'traverseU' in Scalaz verwendet wird. Das klingt genau wie das, was Sie versuchen zu tun. –

Antwort

9

Wie von Ben James erwähnt, suchen Sie nach Miles Sabins Unapply trick. Here ist es in Scalaz Repo. Here'straverseU, mit seiner Hilfe implementiert. Here sind einige Beispielverwendungen. Und hier ist meine lückenhaft (hoffentlich richtige) -Implementierung für Ihren speziellen Fall (Anmerkung: Ich habe umbenannt Ihre Applicative zu ApplicativeTest nicht mit Applicative, definierte in scalaz stören):

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) 

ich immer noch nicht wissen, wie man machen Sie es Either[String, Unit] anstelle von Product with Serializable with scala.util.Either[String,Unit] anderen als streng Typ in einem der Fälle wie ich auf dieser Linie sagen: case 5 => Left("x"): Either[String, Unit].

+1

Hier ist ein weiteres Beispiel von "Unapply" in Aktion: http://StackOverflow.com/Questions/14924707/How-to-Write-a-Scalaz-isemppty-Parameter-for-Generic-Types –

+0

Perfekt, danke! Wie Miles mit diesem Zeug kommt, werde ich nie erfahren. – Lachlan

+0

Hier ist eine andere: http://StackOverflow.com/a/16095159/1011414 – mergeconflict

Verwandte Themen