magnolia icon indicating copy to clipboard operation
magnolia copied to clipboard

How to use auto-derivation with contravariant TC

Open ghostbuster91 opened this issue 4 years ago • 10 comments

Hi,

I am struggling with the migration of diffx to scala3, especially with the derivation mechanism. I did a quick check and it seems that changing the Eq tc from magnolia examples also doesn't work. It fails with the following error:

[error] -- Error: /home/kghost0/workspace/magnolia/src/test/tests.scala:295:34 ---------
[error] 295 |      val res = summon[Eq[Person]].equal(Person("John Smith", 34), Person("", 0))
[error]     |                                  ^
[error]     |no implicit argument of type magnolia1.examples.Eq[magnolia1.tests.Person] was found for parameter x of method summon in object Predef.
[error]     |I found:
[error]     |
[error]     |    magnolia1.examples.Eq.autoDerived[A](
[error]     |      /* missing */
[error]     |        summon[
[error]     |          deriving.Mirror{
[error]     |            MirroredType >: magnolia1.tests.Person; 
[error]     |              MirroredMonoType >: magnolia1.tests.Person
[error]     |            ; MirroredElemTypes <: Tuple
[error]     |          }
[error]     |        ]
[error]     |    )
[error]     |
[error]     |But no implicit values were found that match type deriving.Mirror{
[error]     |  MirroredType >: magnolia1.tests.Person; 
[error]     |    MirroredMonoType >: magnolia1.tests.Person
[error]     |  ; MirroredElemTypes <: Tuple
[error]     |}.

Am I missing something? Has somebody tried already something like that?

Full code to reproduce this error can be found here: https://github.com/ghostbuster91/magnolia/tree/contravariant-derivation-3

Notice that calling derivation explicitly Eq.derived[Person] works without any problems.

ghostbuster91 avatar Sep 12 '21 14:09 ghostbuster91

HI, this might be connected to https://github.com/lampepfl/dotty/issues/13146

KacperFKorban avatar Sep 12 '21 15:09 KacperFKorban

Well yes and no :) In the linked issue it is said that this is due to the limitation of the type inference. Providing more information to the compiler using alternative syntax solves this problem.

In my linked example I have already done it for the Tree ADT where new syntax for derivation

sealed trait Tree[+T] derives Eq
object Tree:
  given [T: [X] =>> Show[String, X]] : Show[String, Tree[T]] = Show.derived

was replaced with

sealed trait Tree[+T]
object Tree:
  given [T: [X] =>> Show[String, X]] : Show[String, Tree[T]] = Show.derived
  given lEq[T: Eq] : Eq[Leaf[T]] = Eq.derived[Leaf[T]]
  given bEq[T: Eq] : Eq[Branch[T]] = Eq.derived[Branch[T]]
  given tEq[T : Eq]: Eq[Tree[T]] = Eq.derived[Tree[T]]

But the Eq tc implements AutoDerivation from magnolia so it should be possible to use it without any additional definitions for the Entity ADT.

Last but not least it seems like a good idea to incorporate some tests against contravariant type-classes into magnolia's test suite.

ghostbuster91 avatar Sep 13 '21 07:09 ghostbuster91

@ghostbuster91 what if you add an explicit type parameter to derived? e.g. Show.derived[[X] =>> Show[String, X]]]?

adamw avatar Sep 13 '21 07:09 adamw

@adamw I mentioned the change in Eq definition for Tree ADT, because without that the project didn't want to compile after making Eq tc contravariant (and it is connected with the issue @KacperFKorban linked). As soon as I did it, it worked. The second change which I made was to replace single line in tests: val res = Eq.derived[Entity].equal(Person("John Smith", 34), Person("", 0))

with summoning an implicit instant: val res = summon[Eq[Person]].equal(Person("John Smith", 34), Person("", 0))

This change alone (without going contravariant) works, but combined with contravariant modification it fails to compile. Sorry if that wasn't clear from my original issue.

All the changes can be seen in this single commit: https://github.com/ghostbuster91/magnolia/commit/3e5c919c9d528580f64162fc77be470ce9b2ebc0

ghostbuster91 avatar Sep 13 '21 08:09 ghostbuster91

@ghostbuster91 Ok, so the problem actually involves a hierarchy that wasn't edited in the commit. I must have missed it before, sorry :sweat_smile: Adding explicit instances for Entity seems to solve the problem.

object Entity:
  given Eq[Entity] = Eq.derived[Entity]
  given Eq[Person] = Eq.derived[Person]
  given Eq[Company] = Eq.derived[Company]

KacperFKorban avatar Sep 13 '21 08:09 KacperFKorban

No problem :) But that seems like a semi-auto derivation, doesn't it?

ghostbuster91 avatar Sep 13 '21 08:09 ghostbuster91

Yes, it does. But that feels like a dotty bug since the instance is generated correctly. The only problem is with finding already existing instances.

KacperFKorban avatar Sep 14 '21 10:09 KacperFKorban

Came here from diffx. Thanks for looking at this @ghostbuster91 and @KacperFKorban. Do we have a way forward or are we still blocked by https://github.com/lampepfl/dotty/issues/13146? Happy to help out if needed

dantb avatar Oct 07 '21 09:10 dantb

Hi @dantb

I created a new scala-3 migration branch (https://github.com/softwaremill/diffx/pull/311) as the previous one got a little bit stale and messy. However, the old one was very useful as it showed some problematic areas. With regards to what is there still needed to be done please refer to the TODO list in the PR. Any help would be greatly appreciated :)

When it comes to blockers, auto-derivation seems to be out of the game, but semi-auto generally works.

@KacperFKorban Do you think that lampepfl/dotty#13146 covers that problem with auto-derivation or should we create a separate issue?

ghostbuster91 avatar Oct 09 '21 20:10 ghostbuster91

I think that this dotty issue is unlikely to be solved in the near future. It seams like a limitation and not a bug. Maybe we will have to hack something on magnolia side.

KacperFKorban avatar Oct 11 '21 06:10 KacperFKorban