Skip to content

Instantly share code, notes, and snippets.

@LMnet
Last active November 8, 2019 05:33
Show Gist options
  • Save LMnet/2be88b61078a5cc1416312850093166c to your computer and use it in GitHub Desktop.
Save LMnet/2be88b61078a5cc1416312850093166c to your computer and use it in GitHub Desktop.
Doobie readWithDefaults
/**
* Summon `Read[A]` instance for case class `A` with default parameters.
* {{{
* case class A(a: String, b: Boolean = false)
* implicit val aRead: Read[A] = readWithDefaults[A]()
* }}}
*/
class readWithDefaults[A] {
def apply[
Repr <: HList,
Defaults <: HList,
ZippedIn <: HList,
WithDefaults <: HList,
ZippedOut <: HList,
Result <: HList
]()(implicit
generic: Generic.Aux[A, Repr],
defaults: Default.Aux[A, Defaults],
zipperIn: Zip.Aux[Repr :: Defaults :: HNil, ZippedIn],
mapperIn: Mapper.Aux[readWithDefaults.MapIn.type, ZippedIn, WithDefaults],
read: Lazy[Read[WithDefaults]],
zipperOut: Zip.Aux[WithDefaults :: Defaults :: HNil, ZippedOut],
mapperOut: Mapper.Aux[readWithDefaults.MapOut.type, ZippedOut, Result],
ev: Result =:= Repr,
): Read[A] = {
val (_, _) = (zipperIn, mapperIn) // to prevent unused warning
read.value.map { withDefaults =>
generic.from((withDefaults zip defaults()).map(readWithDefaults.MapOut))
}
}
}
object readWithDefaults {
def apply[A]: readWithDefaults[A] = new readWithDefaults[A]
private object MapIn extends Poly1 {
implicit def withDefault[A]: Case.Aux[(A, Some[A]), Option[A]] =
at { case (a, _) =>
Some(a)
}
implicit def withoutDefault[A]: Case.Aux[(A, None.type), A] =
at { case (a, _) =>
a
}
}
private object MapOut extends Poly1 {
implicit def withDefault[A]: Case.Aux[(Option[A], Some[A]), A] =
at { case (value, Some(defaultValue)) =>
value.getOrElse(defaultValue)
}
implicit def withoutDefault[A]: Case.Aux[(A, None.type), A] =
at { case (value, _) =>
value
}
}
}
@LMnet
Copy link
Author

LMnet commented Nov 8, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment