object Test extends App {
  //
  // An attempt to workaround scala/bug#2712, foiled by scala/bug#3346
  //
  trait TC[M[_]]

  type EitherInt[A] = Either[Int, A]

  implicit object EitherTC extends TC[EitherInt]

  def foo[M[_]: TC, A](ma: M[A]) = ()

  val eii: Either[Int, String] = Right("")

  foo[EitherInt, String](eii)

  // This one needs scala/bug#2712 Higher Order Unification
  //foo(eii) // not inferred

  // A workaround is to provide a set of implicit conversions that take values
  // based on type constructors of various shapes, and search for the
  // type class instances.
  //
  // This is the approach taken by scalaz7.

  trait TCValue[M[_], A] {
    implicit def self: M[A]
    def M: TC[M]

    // instead of `foo(eii)`, we'll try `eii.foo`
    def foo[M[_], A] = ()
  }


  implicit def ToTCValue[M[_], A](ma: M[A])(implicit M0: TC[M]) = new TCValue[M, A] {
    implicit val M = M0
    val self = ma
  }
  implicit def ToTCValueBin1[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[A, α]})#λ]): TCValue[({type λ[α] = M[A, α]})#λ, B] = new TCValue[({type λ[α]=M[A, α]})#λ, B] {
    implicit val M = M0
    val self = ma
  }
  implicit def ToTCValueBin2[M[_, _], A, B](ma: M[A, B])(implicit M0: TC[({type λ[α]=M[α, B]})#λ]): TCValue[({type λ[α]=M[α, B]})#λ, A] = new TCValue[({type λ[α]=M[α, B]})#λ, A] {
    implicit val M = M0
    val self = ma
  }


  ToTCValueBin1(eii).foo

  // as expected, could not find implicit parameter
  // ToTCValueBin2(eii).bar

  // error: implicit conversions are not applicable because they are ambiguous, both method ToTCValueBin1 ... and  method ToTCValueBin2
  //        annoying!!
  //        https://github.com/scala/bug/issues/3346
  //
  // Works if we remove ToTCValueBin2
  //
  eii.bar
}
