Is is possible to improve type inference for partially applied types in Scala?

前端 未结 1 762
温柔的废话
温柔的废话 2020-12-31 19:23

I\'m trying to improve the type inference of the traverse_ function in the code below:

import scala.language.higherKinds

trait Applicative[AF[_         


        
1条回答
  •  爱一瞬间的悲伤
    2020-12-31 19:37

    As pointed out by Ben James, you are looking for Miles Sabin's Unapply trick. Here it is in scalaz repo. Here's traverseU, implemented with its help. Here are some example usages. And here's my sketchy (hopefully correct) implementation for your particular case (note: I've renamed your Applicative to ApplicativeTest not to interfere with Applicative, defined in 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)
    

    I still don't know how to make it infer Either[String, Unit] instead of Product with Serializable with scala.util.Either[String,Unit] other than strictly specify type in one of the cases like I did on this line: case 5 => Left("x"): Either[String, Unit].

    0 讨论(0)
提交回复
热议问题