Search code examples
scalashapelessscala-3

Using K0.ProductInstances in shapeless3


I have a library with typeclasses that I am migrating to Scala 3 using shapeless-3. One of my typeclasses is:

trait Parser[T] {

  def parse(ctx: Context): (Option[T], Context)

}

where Option[T] represents an optionally parsed T and Context wraps the parsing state (e.g. index in a string)

I define some givens for primitive and basic types and eventually have to define a defaultGen for products:

given defaultGen[T](using inst: K0.ProductInstances[Parser, T]): Parser[T] with
  def parse(ctx: Context) = ???

What should I replace ??? with? I tried inst.construct, inst.map but I am unable to make it work without having the compiler complaining with errors such as:

[error] 399 |    def parse(ctx: Context) = inst.construct([t] => (p: Parser[t]) => p.parse(ctx))
[error]     |                                                                                  ^
[error]     |   Found:    [t] => (sweet.delights.parsing.Parser[t]) => (Option[t],
[error]     |     sweet.delights.parsing.Context
[error]     |   )
[error]     |   Required: [t] => (sweet.delights.parsing.Parser[t²]) => t²
[error]     |
[error]     |   where:    t  is a type variable
[error]     |             t² is a type variable with constraint

Solution

  • Type mismatch means that you should use not construct. Try unfold

    given defaultGen[T](using inst: K0.ProductInstances[Parser, T]): Parser[T] with
      def parse(ctx: Context) = inst.unfold[Context](ctx)(
        [t] => (c: Context, p: Parser[t]) => p.parse(c).swap
      ).swap
    

    See other examples:

    How to access parameter list of case class in a dotty macro

    https://github.com/typelevel/shapeless-3/blob/main/modules/deriving/src/test/scala/shapeless3/deriving/type-classes.scala

    https://github.com/satabin/fs2-data/blob/main/csv/generic/shared/src/main/scala-3/fs2/data/csv/generic/semiauto.scala