2

I want to derive instances of some type (Decoder[A]) for arbitrary case classes using shapeless.

trait Decoder[A] extends Serializable { self =>
  def decode(source: String): Either[Throwable, A]
}

If i don't consider the default values for case class then all is going well using the basic approach:

  final implicit def genericDecoder[A, H <: HList](
    implicit gen: LabelledGeneric.Aux[A, H],
    hDecoder: Lazy[Decoder[H]]
  ): Decoder[A] = value => hDecoder.value.decode(value).map(gen.from)

  final implicit val hnilDecoder: Decoder[HNil] = ???

  final implicit def hlistDecoder[K <: Symbol, H, T <: HList](
    implicit witness: Witness.Aux[K],
    hDecoder: Lazy[Decoder[H]],
    tDecoder: Lazy[Decoder[T]]
  ): Decoder[FieldType[K, H] :: T] = ???

Now i want to be able to use a default value for field which can't be decoded. In this case i tried this approach (add additional abstraction layer):

trait DecoderWithDefaults[A, B] extends Serializable {
  def decode(value: String, defaults: B): Either[Throwable, A]
}

  final implicit def genericDecoder[A, H <: HList, HD <: HList](
    implicit gen: LabelledGeneric.Aux[A, H],
    defaults: Default.AsOptions.Aux[A, HD],
    hDecoder: Lazy[DecoderWithDefaults[H, HD]]
  ): Decoder[A] = value => hDecoder.value.decode(value, defaults()).map(gen.from)

  final implicit val hnilDecoder: DecoderWithDefaults[HNil, HNil] = (_, _) => Right(HNil)

  final implicit def hlistDecoder[K <: Symbol, H, T <: HList, TD <: HList](
    implicit witness: Witness.Aux[K],
    hDecoder: Lazy[DecoderWithDefaults[H, Option[H]]],
    tDecoder: Lazy[DecoderWithDefaults[T, TD]]
  ): DecoderWithDefaults[FieldType[K, H] :: T, Option[H] :: TD] = ???

So, my question is: is it possible to achieve the same but without using additional abstraction layer (like DecoderWithDefaults in this case)? Something like:

  final implicit def hlistDecoder[K <: Symbol, H, T <: HList](
    implicit witness: Witness.Aux[K],
    defaultValueHead: Option[H],
    hDecoder: Lazy[Decoder[H]],
    tDecoder: Lazy[Decoder[T]]
  ): Decoder[FieldType[K, H] :: T] = ???
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
Nikita Ryanov
  • 1,520
  • 3
  • 17
  • 34
  • Implicit of type `Option[H]` is very strange (and can't be a good idea). – Dmytro Mitin Aug 13 '20 at 21:50
  • @DmytroMitin yeah, i didn't know how to describe what i want more correct, so i used `Option[H]` as example. Maybe it would be better if i used something like `Witness.Aux[Option[H]]` for sample – Nikita Ryanov Aug 13 '20 at 21:53
  • But such implicit can't be found. Implicit `Witness.Aux[T]` can be found when `T` is a singleton type. `Option[H]` is not a singleton type. – Dmytro Mitin Aug 13 '20 at 22:02
  • 1
    Two type classes (one for case class types, another for `HList` types) seem to be natural. – Dmytro Mitin Aug 13 '20 at 22:04
  • Seems yes. I tried to find out if there is another way to do this, because i've found that this approach is difficult to extend. For example, if i want to process not only default values, but also field annotations then i should add another abstraction layer (or modify existing one) for that instead of simple adding some lines of implicits values for product derivation – Nikita Ryanov Aug 13 '20 at 22:13

0 Answers0