oleg-py / meow-mtl

Next Level MTL for Scala
MIT License
163 stars 21 forks source link

Ambiguous implicits in some cases #11

Open kubukoz opened 5 years ago

kubukoz commented 5 years ago

Recently, I encountered this behavior:

//given
case class AppConfig(foo: FooConfig)
case class FooConfig(value: Int)

//when
implicit val readAppConfig: ApplicativeAsk[F, AppConfig] = new DefaultApplicativeAsk[F, AppConfig] {
  override val applicative: Applicative[F] = F
  override val ask: F[AppConfig] = config.pure[F]
}

//comment this line and the last line doesn't compile
implicitly[ApplicativeAsk[F, AppConfig]]

import com.olegpy.meow.hierarchy._
ApplicativeAsk[F, FooConfig]

The error I'd get if not for the implicitly[ line:

diverging implicit expansion for type cats.mtl.ApplicativeAsk[F,FooConfig]
[error] starting with method deriveApplicativeAsk in trait Priority2
[error] ApplicativeAsk[F, FooConfig]

I don't have any cats-mtl imports (apart from the type names ApplicativeAsk/DefaultApplicativeAsk). It appears like the compile isn't able to use the correct derivation method unless there's a hint from the user (being the implicitly[] call.

I'm using Scala 2.12.7 and meow-mtl 0.2.0. Do you think it's a compiler bug or something related to meow itself?

oleg-py commented 5 years ago

Wow. This is crazy. And thanks for the report, btw. Seems to be caused by an odd interaction with ApplicativeLocal inheriting ApplicativeAsk (this is the only typeclass using inheritance instead of composition), where compiler tries both methods when asked for ApplicativeAsk only. I'll try to filter that out if I figure out how or drop the ApplicativeLocal altogether.

The implicitly call seems to cause side-effect in order of implicit resolution, which in your case does not go into the loop where it's trying to derive ask from local over and over.

I'll take a look at some unspecified date in the future, hopefully this month :)

Meanwhile, workaround I can propose right now is to cherry-pick imports:

import com.olegpy.meow.hierarchy.deriveApplicativeAsk
// or
import com.olegpy.meow.hierarchy.{deriveApplicativeLocal => _, _}
kubukoz commented 5 years ago

Worth noting: in a similar case, I moved the hierarchy import up (to the file-global imports), and it worked. I'll use cherry-picked imports in other cases, then :) thanks!

kubukoz commented 5 years ago

Just hit this again, and no matter where I'd put the imports (or whether i'd use the cherry-picked ones), it didn't work :(

oleg-py commented 5 years ago

@kubukoz care to provide more details?

kubukoz commented 5 years ago

There's not much to it... but I fear it might be a strange interaction with bm4. Let me check...

kubukoz commented 5 years ago

Disabled bm4 so it's not that.

What I have:

ApplicativeAsk[F, Config] in implicit scope,

then I do ApplicativeAsk[F, FooConfig] (FooConfig is the type of one of the fields in Config) and it doesn't work. I tried the trick with explicitly summoning the root ask, but it didn't help either.

The error is again implicit error or diverging implicit expansion, depending on the cherry-pickiness of imports.