3

I'm trying to extend the Parser type class from this blog post to support nested case classes parsing. Since I'm new to shapeless, I've decided to start with the non-general case, that would only support a case class with 2 fields that are themselves case classes. To illustrate this better, here's the case classes definition first:

case class Person(name: String, age: Int)
case class Family(husband: Person, wife: Person)

In addition to the code from that blog post, I've created one more implicit function that is supposed to support my case, parsing Family class from the string like "Hans,70|Emmy,60" by dividing the string in 2 parts by '|', then parsing those 2 separate parts independently, and at last combining the result. Here how it looks like:

implicit def nestedCaseClassParser[A, B, C]
  (
    implicit pb: Parser[B],
    pc: Parser[C],
    gen: Generic.Aux[A, B :: C :: HNil]
  ): Parser[A] = new Parser[A] {
    override def apply(s: String): Option[A] = {
      val tmp = s.span(_ != '|') match {
        case (h, t) =>
          for {
            a <- pb(h)
            b <- pc(t.substring(1))
          } yield a :: b :: HNil
      }
      tmp.map(gen.from)
    }
  }

Then, when I'm trying to parse it like this:

val p = Parser.apply[Family]("hello,1|hello,1")

I'm getting the following compilation error:

<console>:83: error: diverging implicit expansion for type Parser[Family]
starting with method nestedCaseClassParser in object Parser
       val p = Parser.apply[Family]("hello,1|hello,1")
                             ^

I'm not asking for a full solution for the problem of fully generic nested case class parsing, as I'm eager to find it myself, but I'd really appreciate an explanation of what's going wrong with this code, and how can I make it work on this simple level.

EDIT: Since I've been able to overcome the initial problem, I've created a separate questions for the thing I've run into next.

Community
  • 1
  • 1
Haspemulator
  • 11,050
  • 9
  • 49
  • 76
  • One issue is that both `hconsParser` and your `nestedCaseClassParser` provide instances for `Person` and `Family`, and there's no (simple) way to prioritize them so that the former applies to `Person` and the latter to `Family`. – Travis Brown Nov 18 '15 at 21:51
  • @TravisBrown, FWIW I've copied the code from your blog post verbatim, and I see the same error when I try to parse `BookBook` instance. In your example you get the `None`, but I get the compilation error about diverging implicit expansion. How come? – Haspemulator Nov 19 '15 at 12:32
  • You need to define the code in a single `:paste` session—sorry I didn't make that clear. I'll update the post. – Travis Brown Nov 19 '15 at 15:03
  • I did that in the Intellij IDEA worksheet, actually, and that usually works fine. Anyways, I repeated the same in console with `:paste`, no change. I've created a gist with your code in one chunk, it's here: https://gist.github.com/RomanIakovlev/ebf3bf2cba129f25c7db. Still gives me the diverging implicits error. – Haspemulator Nov 19 '15 at 15:19
  • Sorry, the parser code needs to be defined together, but because of compiler bugs that affect the macro that creates `Generic` instances, the last two lines need to be separate from each other. This is all unfortunately very confusing, but luckily most of these issues don't come up outside of the REPL. – Travis Brown Nov 19 '15 at 15:49
  • Yay, now it works as you told! Thanks, I'll continue the investigations. – Haspemulator Nov 19 '15 at 15:58
  • Okay, not so quick... I've made it work in REPL by declaring `BookBook` and calling its parser in 2 separate `:paste`, but in real project I still face this error. I put all case classes into separate files, and `Parser` is also in a separate file, and I put all the implicits into the `Parser` companion object. In yet another separate file I create a class that parses the `BookBook`, and see this dreaded `diverging implicit expansion` once again. – Haspemulator Nov 19 '15 at 16:57
  • I've put the whole thing here: https://github.com/RomanIakovlev/shapeless_playground. – Haspemulator Nov 19 '15 at 17:17
  • @TravisBrown, I know this is getting too long, but I've even tried to put the `Parser` into a separate sbt subproject, and it still doesn't compile with the `Parser[BookBook]("whatever")`. How can there be such difference between REPL and the project, and in the way that's opposite from the usual, i.e. REPL works better? – Haspemulator Nov 20 '15 at 10:31

0 Answers0