Scala 3 typeclass derivation - #3820
Conversation
|
|
||
| import scala.compiletime.{erasedValue, summonInline, constValue} | ||
|
|
||
| inline private[derivation] def summonAll[T <: Tuple, F[_]]: List[F[Any]] = |
There was a problem hiding this comment.
Isn't that scala.compileTime.summonAll[Tuple.Map[T, F]].toList?
I believe you could also drop the toList, tuples are pretty powerful in Scala 3, even zipping is supported.
Anyway, I very much like the initiative of adding this to cats and will watch this PR (and stop nitpicking now 😉)!
There was a problem hiding this comment.
You're probably right :) I've copied my older code verbatim to test if this is binary compatible and if it works, so now I'm leisurely researching all the ways to make this more concise and faster :)
There was a problem hiding this comment.
I managed to remove the lists and iterators and instead only use the tuples (plus inlining the whole thing).
Now there's a concern about inlining the derived call when deriving the coproduct, not sure if this can have any repercussions
|
I think there are a few cases we can handle:
It would be cool to have derivation methods for those cases, and the majority of our type classes (Order, Monoid, Eq, ... ) are one of these cases. |
|
Now that I had more thoughts about this:
I'd love to hear maintainers' thoughts (and going to bring in @joroKr21 into the discussion), and may be this PR can be closed. |
|
I was just going to suggest giving shapeless 3 a try (I haven't yet myself 😄). One edge case I was thinking about - there are actually more ways to one to derive |
|
Here is how it could look like in kittens with shapeless 3:
package cats.derived
import alleycats._
import cats._
import shapeless3.deriving.K0
object empty extends EmptyDerivation
object semigroup extends SemigroupDerivation
object monoid extends MonoidDerivation
object all extends
EmptyDerivation,
SemigroupDerivation,
MonoidDerivation
class ProductEmpty[F[x] <: Empty[x], A](
using inst: K0.ProductInstances[F, A]
) extends Empty[A]:
val empty: A = inst.construct([A] => (F: F[A]) => F.empty)
trait EmptyDerivation:
extension (E: Empty.type)
inline def derived[A](using gen: K0.ProductGeneric[A]): Empty[A] =
ProductEmpty(using K0.mkProductInstances)
class ProductSemigroup[F[x] <: Semigroup[x], A](
using inst: K0.ProductInstances[F, A]
) extends Semigroup[A]:
def combine(x: A, y: A): A =
inst.map2(x, y)([A] => (F: F[A], x: A, y: A) => F.combine(x, y))
trait SemigroupDerivation:
extension (S: Semigroup.type)
inline def derived[A](using gen: K0.ProductGeneric[A]): Semigroup[A] =
ProductSemigroup(using K0.mkProductInstances)
class ProductMonoid[F[x] <: Monoid[x], A](
using inst: K0.ProductInstances[F, A]
) extends ProductSemigroup[F, A], Monoid[A]:
val empty: A = inst.construct([A] => (F: F[A]) => F.empty)
trait MonoidDerivation:
extension (M: Monoid.type)
inline def derived[A](using gen: K0.ProductGeneric[A]): Monoid[A] =
ProductMonoid(using K0.mkProductInstances) |
|
Just to make my opinion clear, I'd prefer if we could get something for this for scala 3 in cats. Kittens is great, but another dependency is another moment to get buy-in to commit. I think if we can offer this in vanilla scala 3 in cats we should do so. |
|
We could if we accept Shapeless 3 as a dependency. |
|
Or we could shade shapeless 3 to avoid dependency clashes |
Do you mean clashes with Shapeless 2? I think actually there should be no clash. |
|
No, I mean clashes against future versions of Shapeless 3. The binary compatibility story of Shapeless 2 caused lots of pain for certain folks -- in particular, some Cats maintainers who were stuck on an old version of Shapeless due to Spark pulling it in. If we were to have cats-kernel and cats-core depend on Shapeless 3, I'd expect a number of folks to object due to those experiences. |
|
I guess it's not a problem to shade |
|
It's not a question of semantic versioning but rather stability of the dependency. If shapeless3-deriving isn't going to break binary compatibility until / coincident with Cats 3, then the dependency isn't a concern. Would need to hear more from you or @milessabin w.r.t. long term binary stability. |
|
I didn't work on Shapeless 3 but I would expect once 3.0 is out for it to remain stable for a long time. But Miles knows better 😛 |
|
shapeless has maintained patch version number bincompat for years now. To the best of my knowledge the only issues have been things like, eg., Spark depending on ancient versions. |
I think so too. shapeless 3.0 is modularized, and I think cats would only need to depend on shapeless-3 deriving, which I wouldn't expect to evolve any faster than cats. But I think it would be a shame to duplicate the fantastic work that @joroKr21 and @kailuowang have done on kittens. If people don't want to add a dependency then I think it might make more sense to make kittens a cats module. |
|
Right now we are collaborating in Kittens with @TimWSpence on Scala 3 derivation. |
|
Apropos @joroKr21's reference to the work going on in kittens, see here: typelevel/kittens#330. |
|
Just wondering if we're any closer to a concensus on this? |
|
Any thoughts on this question:
|
|
I think derivation out-of-the-box in Cats would be fantastic, particularly for Scala 3 with |
|
What is it with you, Arman, and closing my PRs :D |
(This is very early, Opening it to check the approach and to use the CI)
Goals:
And making sure to achieve parity with kittens.
TODO:
fromProductis the only way to do so