-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Document how to do Semi-Auto Derivation with Typeclasses #7875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It would be good to minimize this further. Right now it's hard to tell whether this is a bug or not. |
The example is incomplete and cannot be compiled. @deusaquilus could you create a version that contains all the code needed to compile it and try to minimize it a bit more. |
Please give me a couple days and I will reproduce the issue in Scastie |
As it turns out, what I tried doing here is possible by using (A bit more detail: Basically, the whole pattern of "summon a Mirror for T, otherwise T is a leaf node" is completely superseded by Dotty's generic-derivation mechanism. The example I posted at the top can be fulfilled by generic derivation. The only problem was, from the Documentation I thought it was necessary to provided typeclass instances via package example.eqalternate
import scala.deriving._
import scala.quoted._
import scala.quoted.matching._
import scala.compiletime.{summonFrom, erasedValue}
trait Eq[T] {
def eqv(x: T, y: T): Boolean
}
object Eq {
given Eq[String] {
def eqv(x: String, y: String) = x == y
}
given Eq[Int] {
def eqv(x: Int, y: Int) = x == y
}
def check(elem: Eq[_])(x: Any, y: Any): Boolean =
elem.asInstanceOf[Eq[Any]].eqv(x, y)
def iterator[T](p: T) = p.asInstanceOf[Product].productIterator
def eqSum[T](s: Mirror.SumOf[T], elems: List[Eq[_]]): Eq[T] =
new Eq[T] {
def eqv(x: T, y: T): Boolean = {
val ordx = s.ordinal(x)
(s.ordinal(y) == ordx) && check(elems(ordx))(x, y)
}
}
def eqProduct[T](p: Mirror.ProductOf[T], elems: List[Eq[_]]): Eq[T] =
new Eq[T] {
def eqv(x: T, y: T): Boolean =
iterator(x).zip(iterator(y)).zip(elems.iterator).forall {
case ((x, y), elem) => check(elem)(x, y)
}
}
inline def summonInstance[T]: Eq[T] = summonFrom {
case t: Eq[T] => t
}
inline def summonAll[T <: Tuple]: List[Eq[_]] = inline erasedValue[T] match {
case _: Unit => Nil
case _: (t *: ts) => summonInstance[t] :: summonAll[ts]
}
inline def derived[T]: Eq[T] =
summonFrom {
case ev: Mirror.Of[T] =>
inline ev match {
case s: Mirror.SumOf[T] => eqSum(s, summonAll[s.MirroredElemTypes])
case p: Mirror.ProductOf[T] => eqProduct(p, summonAll[p.MirroredElemTypes])
}
}
}
object Macro_4 {
implicit inline def eqGen[T]: Eq[T] = Eq.derived
inline def [T](x: =>T) === (y: =>T): Boolean = {
val eq = summon[Eq[T]]
eq.eqv(x, y)
}
} Then you can just do: package example.eqalternate
@main def eqTest() = {
case class Address(street: String) // Hurrah! Don't need to do 'derived'
case class Person(name: String, address: Address) // Hurrah! Don't need to do 'derived'
import Macro_4._
println( Person("Joe", Address("123")) === Person("Joe", Address("123")) )
} I did not know that the above pattern was possible. Please add it to the documentation page and/or make a regression test for it. |
Now I see! Where do you think its best to mention this in the documentation so that it is clear? Can you leave a suggestion on #8011 as a comment? And I will incorporate it in that PR! Thx! @deusaquilus |
So a couple of notes about
|
Also, interestingly object Macro_4 {
implicit inline def eqGen[T]: Eq[T] = Eq.derived
// inline given autoEq[T]: Eq[T] = Eq.derived // This does not work!!
inline def [T](x: =>T) === (y: =>T): Boolean = {
val eq = summon[Eq[T]]
eq.eqv(x, y)
}
} |
Here's how I would do it: #8087. |
Uh oh!
There was an error while loading. Please reload this page.
In Quill, we frequently use the following pattern:
For some given type T (note, we do not have an actual instance of T yet!)
I am trying to reproduce this kind of behavior with mirrors as I understand it is no longer possible to do things like
tpe.members.collect
. The trouble is, it isn't working.minimized code
expectation
My expectation is for this code to work properly and go through the children of some type T. Instead, there are several very odd behaviors.
Firstly, I get an implicit resolution error because for some reason, it will match
Fooify[T]
for anyT
.Say I only keep
Fooify[String]
around and comment out the otherFooify
, then, the following error happens:Edit:
As it turns out, you can implement this pattern using generic derivation so this is just a documentation issue. See further below for a better example:
(This is how to do the same thing with typeclass derivation)
Then all you need to do is this:
More Edit:
As it turns out this pattern is well known as auto-derivation. I think it should definitely be documented on the typeclass-derivation page.
The text was updated successfully, but these errors were encountered: