Open Daenyth opened 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.
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!
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.
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:
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?
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.
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
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 :)
Example:
I'd expect it to generate something like: