是否可以改进 Scala 中部分应用类型的类型推断? [英] Is is possible to improve type inference for partially applied types in Scala?

查看:35
本文介绍了是否可以改进 Scala 中部分应用类型的类型推断?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试改进以下代码中 traverse_ 函数的类型推断:

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

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_ 的类型参数:

To get it to compile, I have to specify the type arguments to traverse_:

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

有什么方法可以重构 traverse_ 或代码的任何其他部分,以使类型推断起作用?当类型开始变得越来越复杂时,这会很快变得烦人.

Is there a way I can restructure traverse_, or any other part of the code, to make the type inference work? When the types start getting more complex, this gets annoying fast.

推荐答案

正如 Ben James 所指出的,您正在寻找 Miles Sabin 的 Unapply 技巧.这里在 scalaz 回购中.这里是traverseU,在它的帮助下实现.这里是一些示例用法.这是我针对您的特定情况的粗略(希望是正确的)实现(注意:我已将您的 Applicative 重命名为 ApplicativeTest 以免干扰 Applicative,在 scalaz 中定义):

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)

我仍然不知道如何让它推断 Either[String, Unit] 而不是 Product with Serializable with scala.util.Either[String,Unit]除了像我在这一行所做的那样在其中一种情况下严格指定类型:case 5 =>Left("x"):Either[String, Unit].

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].

这篇关于是否可以改进 Scala 中部分应用类型的类型推断?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆