Skip to content

Instantly share code, notes, and snippets.

@puffnfresh
Last active January 4, 2016 00:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save puffnfresh/8540756 to your computer and use it in GitHub Desktop.
Save puffnfresh/8540756 to your computer and use it in GitHub Desktop.
Minimised scalac crash.
import scalaz._
import Scalaz._
import scalaz.effect._
object Test {
implicit def eitherTMonadIO[M[_], E](implicit M: MonadIO[M]): MonadIO[({type l[A]=EitherT[M, E, A]})#l] = ???
def x = for {
_ <- true whenM IO.putStrLn("World").liftIO[({type e[A]=EitherT[IO, String, A]})#e]
} yield ()
}
/*
scala> :l Test.scala
Loading Test.scala...
import scalaz._
import Scalaz._
import scalaz.effect._
error: type mismatch;
found : scalaz.EitherT[scalaz.effect.IO,String,Unit]
required: ?M
Note that implicit conversions are not applicable because they are ambiguous:
both method ToBitraverseVFromKleisliLike in trait ToBitraverseOps of type [G[_], F[G[_], _, _], A, B](v: F[G,A,B])(implicit F0: scalaz.Bitraverse[[α, β]F[G,α,β]])scalaz.syntax.BitraverseOps[[α, β]F[G,α,β],A,B]
and method ToArrowVFromKleisliLike in trait ToArrowOps of type [G[_], F[G[_], _, _], A, B](v: F[G,A,B])(implicit F0: scalaz.Arrow[[α, β]F[G,α,β]])scalaz.syntax.ArrowOps[[α, β]F[G,α,β],A,B]
are possible conversion functions from scalaz.EitherT[scalaz.effect.IO,String,Unit] to ?M
*/
@retronym
Copy link

That rings a bell, I fixed that in 2.11.

% git diff head
diff --git a/effect/src/main/scala/scalaz/Test.scala b/effect/src/main/scala/scalaz/Test
new file mode 100644
index 0000000..cabc61e
--- /dev/null
+++ b/effect/src/main/scala/scalaz/Test.scala
@@ -0,0 +1,13 @@
+object Test {
+  import scalaz._
+  import Scalaz._
+  import scalaz.effect._
+
+  object Test {
+    implicit def eitherTMonadIO[M[_], E](implicit M: MonadIO[M]): MonadIO[({ type l[A]
+
+    def x = for {
+      _ <- true whenM IO.putStrLn("World").liftIO[({ type e[A] = EitherT[IO, String, A]
+    } yield ()
+  }
+}


% sbt -scala-version 2.11.0-M7 effect/compile
[error] /Users/jason/code/scalaz/effect/src/main/scala/scalaz/Test.scala:10: could not find implicit value for evidence parameter of type scalaz.Applicative[Any]
[error]       _ <- true whenM IO.putStrLn("World").liftIO[({ type e[A] = EitherT[IO, String, A] })#e]
[error]                 ^
[error] one error found

@retronym
Copy link

Fixed the error reporting, at least. The type argument inference for whenM clearly isn't what you're after. It looks ill-kinded, but Any is taken to be kind-polymorphic so that is allowed. It will never infer the partially applied type constructor, however.

@retronym
Copy link

Introducing a type alias to hint the type-constructor inference is usually the best way to deal with this. The following compiles:

object Test {
  import scalaz._
  import Scalaz._
  import scalaz.effect._

  object Test {
    implicit def eitherTMonadIO[M[_], E](implicit M: MonadIO[M]): MonadIO[({ type l[A] = EitherT[M, E, A] })#l] = ???

    type e[A] = EitherT[IO, String, A]
    def x = for {
      _ <- true whenM IO.putStrLn("World").liftIO[e]
    } yield ()
  }
}

@retronym
Copy link

import language._

object Test {
  trait E[A, B]

    def foo[M[_]](mi: M[Int]): M[Int] = mi

  val e: E[Int, Int] = ???

  // M?[Int] ~ Either[Int, Int]
  //
  // no type parameters for method foo: (mi: M[Int])M[Int] exist
  // argument expression's type is not compatible with formal parameter type;
  // found   : Test.E[Int,Int]
  // required: ?M
  // foo(e)

  object Sub {
    implicit class ImplicitClass1[A](val e: E[_, _]) // aka MergeableEither in the stdlib
    // having a non-ambiguous implicit from E[Int, Int] => _[_]
    // is enough for type inference to consider this applicable.
    // the type variable M? accumulates constraints during the entire
    // implicit search rather than just the successful case (!!!),
    // which leads us to solve the constraints:
    // 
    // M? >: (ArrowAssoc, Ensuring, StringFormat, StringAdd, Test.Sub.ImplicitClass1)
    //
    // Only the empty set of upper bounds is actualy material in this case,
    // as M appears in a covariant position in the formal param types of `foo`
    // of `foo`.
    //
    // With no upper bounds, the solution is the GLB(Nil), or:
    //
    // M? = Any
    //
    // The implicit that it found to satisfy `isCompatible` is now long
    // forgotten, and things proceed as if you'd written:
    //
    // foo[Any](e)
    //
    // Thanks to the regrettable kind-polymorphic nature of `Any`, this typechecks!
    //
    // -Ywarn-infer-any on 2.11.x comes highly recommended!
    //
    // BTW, you could probably add this to wart-remover in 2.10 by finding TypeTree-s
    // in type applications where the Type =:= Any but the TypeTree.original == null.
    // This means it was inferred; TypeTrees that originate from Idents/Selects in source
    // have a non-null original.
    foo(e)
  }

  // This part shows how a type alias (not one that's hidden in a type lambda)
  // can help inference.

  // During inference, a type variable representing a type constructor is
  // unified with the series of candidate types until one matches.
  // This is implemented here: https://github.com/scala/scala/blob/v2.10.3-RC3/src/reflect/scala/reflect/internal/Types.scala#L3217
  // Type aliases are unwrapped one-by-one to find case where shape of the
  // LHS and RHS align.
  //
  // If that fails, super types are considered.
  //
  // https://github.com/scala/scala/blob/v2.10.3-RC3/src/reflect/scala/reflect/internal/Types.scala#L3270
  //

  type EInt[A] = E[Int, A]
  val e1: EInt[Int] = e
  // M?[Int] ~ EitherInt[Int]
  // M = EitherInt
  // foo[EitherInt](e)
  foo(e1)

  foo( foo[({type l[A] = E[Int, A]})#l](e) )
  //   `------------Test.E[Int,Int]}-----/
  //
  // The result type of this expression can't refer to the type alias
  // `l` as this would be ill-scoped. As such, the compiler has to
  // expand the type (by existentially abstracting over `l` which in
  // this case is simplified to a dealiasing back to `E[Int, Int]`)
  // As such, the type constructor inference doesn't have a means
  // unify with `M?[Int]` when inferring the type arg for the outer
  // application to `foo`.
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment