scalafunctorscalaztype-aliasbifunctor

Parametrized type alias for Bifunctors


I have a Seq[R] and I wanna split this into a Tuple2[Seq[E], Seq[S]], while I was coding this I thought about the fact that I could use a custom Bifunctor for a tuple of seqs and as exercise tried to code this:

import scalaz.Bifunctor

type MyType[E, S] = (Seq[E], Seq[S])

case class MyVali[E, S](tp: (Seq[E], Seq[S]))(implicit bifunctor: Bifunctor[MyType]) {
  def bimap[C, D](f: (E) => C, g: (S) => D): (Seq[C], Seq[D]) =
    bifunctor.bimap(tp)(f, g)

  def leftMap[C](f: (E) => C): (Seq[C], Seq[S]) =
    bifunctor.leftMap(tp)(f)

  def rightMap[D](g: (S) => D): (Seq[E], Seq[D]) =
    bifunctor.rightMap(tp)(g)

}

val myValBifunctorInstance = new Bifunctor[MyType] {
  override def bimap[A, B, C, D](fab: (Seq[A], Seq[B]))(f: (A) => C, g: (B) => D): (Seq[C], Seq[D]) =
    (fab._1.map(f), fab._2.map(g))
}

MyVali((Seq.empty[String], Seq.empty[Int]))(myValBifunctorInstance).bimap(a => a, b => b)

This works fine but for some reason obscure to me I had to declare a parametrized type alias to make all this compile, namely type MyType[E, S] = (Seq[E], Seq[S]) and I hardly understand why this works while this doesn't:

def myValBimap[E, S] = new Bifunctor[Tuple2[Seq[E], Seq[S]]] {
  override def bimap[A, B, C, D](fab: (A, B))(f: (A) => C, g: (B) => D): (C, D) = ???
}

[error] ... (Seq[E], Seq[S]) takes no type parameters, expected: two

[error] def myValBimap[E, S] = new Bifunctor[Tuple2[Seq[E], Seq[S]]] {

Is the compiler creating a 2-type suspension (like a nested lambda type maybe?) when such a type alias is defined?


Solution

  • def myValBimap[E, S] = new Bifunctor[Tuple2[Seq[E], Seq[S]]] {  ... }
    

    The Tuple2[...] in the Bifunctor above doesn't have two type parameters anymore because the E and S are filled in.

    Eg myValBimap[Int, String] tries to create a Bifunctor[(Seq[Int], Seq[String])] and the type (Seq[Int], Seq[String]) clearly has no two type parameters.

    You could write

    You need an implicit function with a type parameter if you need an additional type, maybe something like :

    implicit def myBimap[F[_]: Functor]: Bifunctor[λ[(α, β) => (F[α], F[β])]] = ???
    

    A simpler example is the Functor for disjunction / \/ which uses a type parameter for the left side :

    implicit def functorDisj[L]: Functor[L \/ ?] = ???
    implicit def functorDisj[L]: Functor[({ type λ[α] = L \/ α })#λ] = ???
    

    About your original problem: It might be possible to map your R to E \/ S and use separate :

    import scalaz._, Scalaz._
    
    val integers = List.range(1,10)
    val stringOrInts: List[String \/ Int] = 
      integers.map(i => if (i % 2 == 0) i.right else i.toString.left)
    
    stringOrInts.separate
    // (List[String], List[Int]) = (List(1, 3, 5, 7, 9),List(2, 4, 6, 8))