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)
Running the above gives:
/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
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(())
}
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.
The Scala compiler can often infer the type of an expression so you don't have to declare it explicitly.
Scala has a built-in type inference mechanism.
Scala compiler can automatically infer types of each variable declared. If the value of a variable is declared in double-quotes it will automatically be inferred as String. Also, the compiler can infer any value in a single quote is inferred as Char.
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]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With