Nicolas Rinaudo
Nicolas Rinaudo

Reputation: 6168

Deriving decoder instances of case classes with a single field

This question is tightly related to this one, but not identical: it's the same context but the opposite problem.

I'm trying to derive a CellDecoder[A], which is essentially a String => A, for As that are case classes with a single field whose type has a CellDecoder.

In order to do this, I need to require:

That is:

implicit def caseClassCellDecoder[A, R, H](implicit
  gen: Generic.Aux[A, R],
  ev: R <:< (H :: HNil),
  d: CellDecoder[H]
): CellDecoder[A] = ???

The issue I'm having is that, once I've turned a String to an H through the relevant CellDecoder instance, I'm kind of stuck: ev allows me to turn an R into an H :: HNil, but not an H :: HNil into an R. Not having an R, I can't use my Generic.Aux[A, R] to get the final A instance.

It turns out that casting my H :: HNil into an R works, but I'm not sure why and can't convince myself that it'll always be the case.

I thought about requiring R to be strictly equal to H :: HNil (that is, to have a R =:= HNil), but then the implicits fail to resolve.

Is there a way to do what I'm trying to do?

Upvotes: 1

Views: 116

Answers (1)

Vered Rosen
Vered Rosen

Reputation: 381

I did use =:= instead of <:< however swapped the sides of R and H::HNil and that seemed to work:

case class CellDecoder[A](decode: String => A)

implicit val stringCellDecoder: CellDecoder[String] = CellDecoder(identity)

implicit def caseClassCellDecoder[A, R, H](implicit
                                           gen: Generic.Aux[A, R],
                                           ev: (H :: HNil) =:= R,
                                           d: CellDecoder[H]
                                            ): CellDecoder[A] = CellDecoder(
  (str: String) => gen.from(ev(d.decode(str)::HNil))
)

So I was able to use it as follows:

case class TestDecoder(test: String)

def doDecode[A: CellDecoder](str: String): A = {
  implicitly[CellDecoder[A]].decode(str)
}

doDecode[TestDecoder]("encoded value")

Upvotes: 2

Related Questions