3

I am trying to write a Functor for Either for academic purposes in Scala. With help of higher-kinded types and type-projections, I managed to write an implementation for Either.

trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

object Functor {
  implicit def eitherFunctor[A] = new Functor[({type λ[α] = Either[A, α]})#λ] {
    override def map[B, C](fa: Either[A, B])(f: B => C) = fa.map(f)
  }
}

def mapAll[F[_], A, B](fa: F[A])(f: A => B)(implicit fe: Functor[F]): F[B] = fe.map(fa)(f)

val right: Either[String, Int] =  Right(2)

mapAll(right)(_ + 2)

Now, the code above does not compile. I am not sure of the reason but the compilation error that I am getting is given below -

Error:(19, 16) type mismatch;
 found   : Either[String,Int]
 required: ?F[?A]
Note that implicit conversions are not applicable because they are ambiguous:
 both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
 and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
 are possible conversion functions from Either[String,Int] to ?F[?A]
  mapAll(right)(_ + 2)

Can someone point what I am not doing right in the code above?

PS: Please do not suggest me to use kind-projector.

Anshul Bajpai
  • 97
  • 1
  • 8
  • 1
    Scala does not support partially applied type constructors. If a method takes an `F[_]`, you must give it an `F[_]`, not an `F[String, _]`. – Ziyang Liu Sep 25 '17 at 13:54
  • @AnshulBajpai Your current version of code compiles and runs in 2.12.3: `mapAll(right)(_ + 2) //Right(4)` – Dmytro Mitin Sep 25 '17 at 14:06
  • @DmytroMitin - I am using scala 2.12.3 I thought it'll work but it didn't. Did you have to turn on any scala compiler options? – Anshul Bajpai Sep 25 '17 at 16:37
  • 1
    @AnshulBajpai Yes, you're right. My build.sbt: `scalaVersion := "2.12.3" scalacOptions ++= Seq("-language:higherKinds", "-language:reflectiveCalls", "-Ypartial-unification")`. Sorry for misleading. – Dmytro Mitin Sep 25 '17 at 18:59
  • @DmytroMitin - only `Ypartial-unification` option was required to make the compilation pass. Much thanks. – Anshul Bajpai Sep 25 '17 at 21:39

3 Answers3

3

You've just been bitten by SI-2712. If you're using Scala >= 2.12.2 just add this line to your build.sbt:

scalacOptions += "-Ypartial-unification"

For other Scala versions you can use this plugin.

lambdista
  • 1,720
  • 7
  • 16
1

Either[+A, +B] is expecting two type parameters(as @Ziyang Liu said), so for your example actually need BiFunctor not Functor, BiFunctor accept two functors and bound the two types.

there is a Bifunctor from Scalaz

trait Bifunctor[F[_, _]]  { self =>
  ////

  /** `map` over both type parameters. */
  def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D]

So you can use this Bifunctor like:

Bifunctor[Either].bimap(Right(1): Either[String, Int])(_.toUpperCase, _ + 1).println

Hope it's helpful for you.

chengpohi
  • 13,467
  • 1
  • 21
  • 40
  • Thanks, I've read about `Bifunctors` and that is another way of doing the similar thing but not the actual thing I want to achieve. But at the moment, I am not looking for a Bifunctor solution. I am pretty sure this can be solved by using Type projections. The downside is this implementation will be Right biased but maybe that's what I want. Also, I'd like to know what is going wrong in my code which will give me more understanding of the problem. If I want to use libraries, I can simply use `cats` which gives `Either` functor. But as I said, I am doing this exercise for academic reasons. – Anshul Bajpai Sep 25 '17 at 14:45
1

As others said, what the compiler is trying to tell you is that the shapes of your types don't match. When you require an F[_], you're requiring a type constructor with a single type parameter, which Either[A, B] doesn't satisfy.

What we need to do is apply a type lambda when applying mapAll, same as we did when we created the instance of the Either functor:

val right: Either[String, Int] = Right(2)

mapAll[({type λ[α]=Either[String, α]})#λ, Int, Int](right)(_ + 2)

We've now squeezed in String and fixed it as the first argument, allowing the type projected type to only need to satisfy our alpha, and now the shapes match.

Of course, we can also use a type alias which would free us from specifying any additional type information when applying mapAll:

type MyEither[A] = Either[String, A]
val right: MyEither[Int] = Right(2)

mapAll(right)(_ + 2)
Yuval Itzchakov
  • 136,303
  • 28
  • 230
  • 296
  • What you mentioned was my first guess to understand the cause of why it didn't work. What you suggested to do with type alias will be the right thing to do if the `Left` is fixed. But we want to generalize it and the solution suggested by @lambdista and @DmytroMitin actually works. But thanks for the detailed elaboration of the problem I was facing. – Anshul Bajpai Sep 25 '17 at 21:45