typelevel / cats-tagless

Library of utilities for tagless final encoded algebras
https://typelevel.org/cats-tagless/
Apache License 2.0
310 stars 41 forks source link

Support derivation for case class #29

Open Daenyth opened 5 years ago

Daenyth commented 5 years ago

Example:

case class ServerDependencies[F[_]](
  foo: FooService[F],
  bar: BarService[F],
  other: NotTaglessThing
)

I'd expect it to generate something like:

def mapK[G[_]](f: F ~> G): ServerDependencies[G] =
  ServerDependencies(foo.mapK(f), bar.mapK(f), other)
joroKr21 commented 5 years ago

That's a good point. I've thought about it. It's also trivial to convert a trait with methods to a case class with functions. But I guess it's way more popular to use traits for final tagless in Scala. I'm not sure about any performance implications.

For case classes we could use shapeless. The problem would be how to integrate that with the traits derivation. Also note that shapeless doesn't support implicit parameters (e.g. Monad[F] - it's almost impossible currently).

But your case class example is not exactly a record of functions.

joroKr21 commented 5 years ago

Ah, I just realized that shapeless doesn't have Generic for this kind of case classes. It has Generic for kind * and Generic1 for kind * -> * but not for (* -> *) -> * which is what we have here. So that would be very difficult!

Krever commented 3 months ago

Hey folks, I would need it for higher kinded data. Do you know if there are any blockers to implement this in Scala 3? If it's possible and not too hard I could give it a shot next week.

joroKr21 commented 3 months ago

Hey folks, I would need it for higher kinded data. Do you know if there are any blockers to implement this in Scala 3? If it's possible and not too hard I could give it a shot next week.

It should be doable with Shapeless 3. I see two complications:

Krever commented 3 months ago

Why do you think shapeless is necessary here? I thought we could just use macros.

Also could we (initially at least) not bother ourselves with fully fledged ADTs hierarchies and support only direct case classes/traits?

joroKr21 commented 3 months ago

Why do you think shapeless is necessary here? I thought we could just use macros.

It's just much easier and type-safe. I think it's fine to start with case classes only.

joroKr21 commented 3 months ago

There is even an example in the tests:

trait FunctorK[H[_[_]]]:
  def mapK[A[_], B[_]](af: H[A])(f: A ~> B): H[B]

object FunctorK:
  inline def apply[H[_[_]]](using fh: FunctorK[H]): FunctorK[H] = fh

  given [T]: FunctorK[K11.Id[T]] with
    def mapK[A[_], B[_]](at: A[T])(f: A ~> B): B[T] = f(at)

  given functorKGen[H[_[_]]](using inst: => K11.Instances[FunctorK, H]): FunctorK[H] with
    def mapK[A[_], B[_]](ha: H[A])(f: A ~> B): H[B] =
      inst.map(ha)([t[_[_]]] => (ft: FunctorK[t], ta: t[A]) => ft.mapK(ta)(f))

  given [T]: FunctorK[K11.Const[T]] with
    def mapK[A[_], B[_]](t: T)(f: A ~> B): T = t

  inline def derived[F[_[_]]](using gen: K11.Generic[F]): FunctorK[F] = functorKGen
Krever commented 2 months ago

Unfortunately, I didn't have a chance to work on it when I hoped. Also in my current work, I decided to embed very minimal FunctorK and SemigroupalK implementation to limit the number of dependencies. But I can at least confirm that the above implementation from shapeless codebase works like a charm :)