0

Given the following from Travis Brown's educational and well-written Type classes and generic derivation:

case class Person(name: String, age: Double)

 trait Parser[A] {
    def apply(s: String): Option[A]
  }

  implicit val hnilParser: Parser[HNil] = new Parser[HNil] {
    def apply(s: String): Option[HNil] = if(s.isEmpty) Some(HNil) else None
  }

  implicit def hconsParser[H: Parser, T <: HList: Parser]: Parser[H :: T] = new Parser[H :: T] {
    def apply(s: String): Option[H :: T] = s.split(",").toList match {
      case cell +: rest => for {
        head <- implicitly[Parser[H]].apply(cell)
        tail <- implicitly[Parser[T]].apply(rest.mkString(","))
      } yield head :: tail
    }
  }

  implicit val stringParser: Parser[String] = new Parser[String] {
    def apply(s: String): Option[String] = Some(s)
  }

  implicit val intParser: Parser[Int] = new Parser[Int] {
    def apply(s: String): Option[Int] = Try(s.toInt).toOption
  }

  implicit val doubleParser: Parser[Double] = new Parser[Double] {
    def apply(s: String): Option[Double] = Try(s.toDouble).toOption
  }

  implicit val booleanParser: Parser[Boolean] = new Parser[Boolean] {
    def apply(s: String): Option[Boolean] = Try(s.toBoolean).toOption
  }

  implicit def caseClassParser[A, R <: HList](implicit gen: Generic[A] { type Repr = R },
                                              reprParser: Parser[R]): Parser[A] =
    new Parser[A] {
      def apply(s: String): Option[A] = reprParser.apply(s).map(gen.from)
    }


  object Parser {
    def apply[A](s: String)(implicit parser: Parser[A]): Option[A] = parser(s)
  }

  implicit val stringParser: Parser[String] = new Parser[String] {
    def apply(s: String): Option[String] = Some(s)
  }

  implicit val intParser: Parser[Int] = new Parser[Int] {
    def apply(s: String): Option[Int] = Try(s.toInt).toOption
  }

  implicit val doubleParser: Parser[Double] = new Parser[Double] {
    def apply(s: String): Option[Double] = Try(s.toDouble).toOption
  }

I was curious to try to get a Parser[X] where X is a case class with a Person argument, i.e. a case class:

case class PersonWrapper(person: Person, x: Int)

Yet I get an error:

scala> Parser[PersonWrapper]("kevin,66,42")
<console>:15: error: diverging implicit expansion for type net.Test.Parser[net.Test.PersonWrapper]
starting with method caseClassParser in object Test
       Parser[PersonWrapper]("kevin,66,42")
                            ^

First, why does this divergent implicit error occur?

Secondly, is it possible to use the above code to get a Parser[PersonWrapper]?

Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384
  • Are you sure you don't have some implicit hanging around in the REPL scope? I don't get the diverging implicit error. – Michael Zajac Jul 31 '16 at 02:28
  • Hmm. Well, what result do you get with `Parser[PersonWrapper]("kevin, 66,42")`? Here's my full result: https://gist.github.com/kevinmeredith/f4995cdf3bc61907bef76182bee69b39 – Kevin Meredith Jul 31 '16 at 02:36

1 Answers1

1

Secondly, is it possible to use the above code to get a Parser[PersonWrapper]?

No, just skip to the end of the article:

scala> case class BookBook(b1: Book, b2: Book)
defined class BookBook

scala> Parser[BookBook]("Hamlet,Shakespeare")
  res7: Option[BookBook] = None

Our format doesn’t support any kind of nesting (at least we haven’t said anything about nesting, and it wouldn’t be trivial), so we don’t actually know how to parse a string into a BookBook...

The problem is that cell in case cell +: rest will only ever be a string with no commas that is passed to implicitly[Parser[H]].apply(cell). For PersonWrapper, this means that the very first cell will attempt to do this:

implicitly[Parser[PersonWrapper]].apply("kevin")

Which will obviously fail to parse. In order to get nested parsers to work, you would need some way to group the cells together prior to applying a Parser[H] to them.

Michael Zajac
  • 55,144
  • 7
  • 113
  • 138