softwaremill / magnolia

Easy, fast, transparent generic derivation of typeclass instances
https://softwaremill.com/open-source/
Apache License 2.0
753 stars 115 forks source link

Best way to deal with ambiguous implicit values with other libraries w.r.t. List, Option, etc? #213

Open nevillelyh opened 4 years ago

nevillelyh commented 4 years ago

This is more of a question for the group.

I made a library that derives common type classes that we use, one of the is cats. https://github.com/spotify/magnolify/tree/master/cats/src/main/scala/magnolify/cats

I want it to be usable out of the box by those not familiar with magnolia, implicit resolution rules, etc. The idea is that users can simply add the following 2 imports, and have type classes instances for everything:

import cats.instances.all._
import magnolify.cats.auto._

The 2nd import goes through some macros that redirect to macro Magnolia.gen[T] like this one https://github.com/spotify/magnolify/blob/master/cats/src/main/scala/magnolify/cats/auto/CatsMacros.scala

However I get error messages like this:

[error] /home/neville/src/spotify/magnolify/cats/src/test/scala/magnolify/cats/test/EqDerivationSpec.scala:39:7: ambiguous implicit values:
[error]  both method catsKernelStdOrderForOption in trait OptionInstances of type [A](implicit evidence$1: cats.kernel.Order[A])cats.kernel.Order[Option[A]]
[error]  and macro method genHash in trait LowPriorityGenHash of type [T]=> cats.Hash[T]
[error]  match expected type magnolify.cats.semiauto.EqDerivation.Typeclass[Option[Boolean]]
[error]   test[Nullable]
[error]       ^
[error] /home/neville/src/spotify/magnolify/cats/src/test/scala/magnolify/cats/test/EqDerivationSpec.scala:40:7: ambiguous implicit values:
[error]  both method catsKernelStdOrderForList in trait ListInstances of type [A](implicit evidence$1: cats.kernel.Order[A])cats.kernel.Order[List[A]]
[error]  and macro method genHash in trait LowPriorityGenHash of type [T]=> cats.Hash[T]
[error]  match expected type magnolify.cats.semiauto.EqDerivation.Typeclass[List[Boolean]]
[error]   test[Repeated]
[error]       ^

Basically Cats already have implicit instances for List[T], Option[T], etc. which are also sealed traits and can be derived with Magnolia. I worked around this by messing with implicit priorities like this, but am wondering if there a better way of handling this?

neko-kai commented 4 years ago

@nevillelyh You can stick a shapeless.LowPriority guard in front of derivation to force it to happen only once every other implicit is tried https://github.com/propensive/magnolia/issues/107#issuecomment-589605500

neko-kai commented 4 years ago

^ LowPriority macro is heavily used in scalacheck-shapeless for the same purpose, i suggest studying it's source code if you have more questions

joroKr21 commented 4 years ago

If you don't want to pull shapeless just for that you can use a simple trick (Refute). See for example in kittens: https://github.com/typelevel/kittens/blob/master/core/src/main/scala/cats/derived/package.scala#L34-L36

Refute is also available in shapeless, but it's really small: https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/refute.scala

nevillelyh commented 4 years ago

Thanks both I'll take a look!