Skip to content

Commit

Permalink
Merge pull request #1967 from tpolecat/opt_in_derivation
Browse files Browse the repository at this point in the history
Opt in auto derivation
  • Loading branch information
jatcwang authored Dec 20, 2023
2 parents 3960a90 + 001b0f1 commit 8b9058b
Show file tree
Hide file tree
Showing 37 changed files with 691 additions and 365 deletions.
File renamed without changes.
5 changes: 3 additions & 2 deletions modules/core/src/main/scala-2/util/GetPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ trait GetPlatform {
C: IsHCons.Aux[L, H, T],
H: Lazy[Get[H]],
E: (H :: HNil) =:= L
): Get[A] = {
): MkGet[A] = {
void(C) // C drives inference but is not used directly
H.value.tmap[A](h => G.from(h :: HNil))
val get = H.value.tmap[A](h => G.from(h :: HNil))
MkGet.lift(get)
}

}
5 changes: 3 additions & 2 deletions modules/core/src/main/scala-2/util/PutPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ trait PutPlatform {
C: IsHCons.Aux[L, H, T],
H: Lazy[Put[H]],
E: (H :: HNil) =:= L
): Put[A] = {
): MkPut[A] = {
void(E) // E is a necessary constraint but isn't used directly
H.value.contramap[A](a => G.to(a).head)
val put = H.value.contramap[A](a => G.to(a).head)
MkPut.lift(put)
}

}
85 changes: 48 additions & 37 deletions modules/core/src/main/scala-2/util/ReadPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,87 @@

package doobie.util

import shapeless.{ HList, HNil, ::, Generic, Lazy, <:!< }
import shapeless.{ HList, HNil, ::, Generic, Lazy, <:!<, OrElse }
import shapeless.labelled.{ field, FieldType }

trait ReadPlatform extends LowerPriorityRead { this: Read.type =>
trait ReadPlatform extends LowerPriorityRead {

implicit def recordRead[K <: Symbol, H, T <: HList](
implicit H: Lazy[Read[H]],
T: Lazy[Read[T]]
): Read[FieldType[K, H] :: T] =
new Read[FieldType[K, H] :: T](
H.value.gets ++ T.value.gets,
(rs, n) => field[K](H.value.unsafeGet(rs, n)) :: T.value.unsafeGet(rs, n + H.value.length)
implicit H: Lazy[Read[H] OrElse MkRead[H]],
T: Lazy[MkRead[T]]
): MkRead[FieldType[K, H] :: T] = {
val head = H.value.unify

new MkRead[FieldType[K, H] :: T](
head.gets ++ T.value.gets,
(rs, n) => field[K](head.unsafeGet(rs, n)) :: T.value.unsafeGet(rs, n + head.length)
)
}

}

trait LowerPriorityRead extends EvenLower { this: Read.type =>
trait LowerPriorityRead extends EvenLower {

implicit def product[H, T <: HList](
implicit H: Lazy[Read[H]],
T: Lazy[Read[T]]
): Read[H :: T] =
new Read[H :: T](
H.value.gets ++ T.value.gets,
(rs, n) => H.value.unsafeGet(rs, n) :: T.value.unsafeGet(rs, n + H.value.length)
implicit H: Lazy[Read[H] OrElse MkRead[H]],
T: Lazy[MkRead[T]]
): MkRead[H :: T] = {
val head = H.value.unify

new MkRead[H :: T](
head.gets ++ T.value.gets,
(rs, n) => head.unsafeGet(rs, n) :: T.value.unsafeGet(rs, n + head.length)
)
}

implicit def emptyProduct: Read[HNil] =
new Read[HNil](Nil, (_, _) => HNil)
implicit val emptyProduct: MkRead[HNil] =
new MkRead[HNil](Nil, (_, _) => HNil)

implicit def generic[F, G](implicit gen: Generic.Aux[F, G], G: Lazy[Read[G]]): Read[F] =
new Read[F](G.value.gets, (rs, n) => gen.from(G.value.unsafeGet(rs, n)))
implicit def generic[F, G](implicit gen: Generic.Aux[F, G], G: Lazy[MkRead[G]]): MkRead[F] =
new MkRead[F](G.value.gets, (rs, n) => gen.from(G.value.unsafeGet(rs, n)))

}

trait EvenLower {

implicit val ohnil: Read[Option[HNil]] =
new Read[Option[HNil]](Nil, (_, _) => Some(HNil))
implicit val ohnil: MkRead[Option[HNil]] =
new MkRead[Option[HNil]](Nil, (_, _) => Some(HNil))

implicit def ohcons1[H, T <: HList](
implicit H: Lazy[Read[Option[H]]],
T: Lazy[Read[Option[T]]],
implicit H: Lazy[Read[Option[H]] OrElse MkRead[Option[H]]],
T: Lazy[MkRead[Option[T]]],
N: H <:!< Option[α] forSome { type α }
): Read[Option[H :: T]] = {
): MkRead[Option[H :: T]] = {
void(N)
new Read[Option[H :: T]](
H.value.gets ++ T.value.gets,
val head = H.value.unify

new MkRead[Option[H :: T]](
head.gets ++ T.value.gets,
(rs, n) =>
for {
h <- H.value.unsafeGet(rs, n)
t <- T.value.unsafeGet(rs, n + H.value.length)
h <- head.unsafeGet(rs, n)
t <- T.value.unsafeGet(rs, n + head.length)
} yield h :: t
)
}

implicit def ohcons2[H, T <: HList](
implicit H: Lazy[Read[Option[H]]],
T: Lazy[Read[Option[T]]]
): Read[Option[Option[H] :: T]] =
new Read[Option[Option[H] :: T]](
H.value.gets ++ T.value.gets,
(rs, n) => T.value.unsafeGet(rs, n + H.value.length).map(H.value.unsafeGet(rs, n) :: _)
implicit H: Lazy[Read[Option[H]] OrElse MkRead[Option[H]]],
T: Lazy[MkRead[Option[T]]]
): MkRead[Option[Option[H] :: T]] = {
val head = H.value.unify

new MkRead[Option[Option[H] :: T]](
head.gets ++ T.value.gets,
(rs, n) => T.value.unsafeGet(rs, n + head.length).map(head.unsafeGet(rs, n) :: _)
)
}

implicit def ogeneric[A, Repr <: HList](
implicit G: Generic.Aux[A, Repr],
B: Lazy[Read[Option[Repr]]]
): Read[Option[A]] =
new Read[Option[A]](B.value.gets, B.value.unsafeGet(_, _).map(G.from))
B: Lazy[MkRead[Option[Repr]]]
): MkRead[Option[A]] =
new MkRead[Option[A]](B.value.gets, B.value.unsafeGet(_, _).map(G.from))

}

91 changes: 49 additions & 42 deletions modules/core/src/main/scala-2/util/WritePlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@

package doobie.util

import shapeless.{ HList, HNil, ::, Generic, Lazy, <:!< }
import shapeless.{ HList, HNil, ::, Generic, Lazy, <:!<, OrElse }
import shapeless.labelled.{ FieldType }

trait WritePlatform extends LowerPriorityWrite {

implicit def recordWrite[K <: Symbol, H, T <: HList](
implicit H: Lazy[Write[H]],
T: Lazy[Write[T]]
): Write[FieldType[K, H] :: T] = {
new Write(
H.value.puts ++ T.value.puts,
{ case h :: t => H.value.toList(h) ++ T.value.toList(t) },
{ case (ps, n, h :: t) => H.value.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + H.value.length, t) },
{ case (rs, n, h :: t) => H.value.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + H.value.length, t) }
implicit H: Lazy[Write[H] OrElse MkWrite[H]],
T: Lazy[MkWrite[T]]
): MkWrite[FieldType[K, H] :: T] = {
val head = H.value.unify

new MkWrite(
head.puts ++ T.value.puts,
{ case h :: t => head.toList(h) ++ T.value.toList(t) },
{ case (ps, n, h :: t) => head.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + head.length, t) },
{ case (rs, n, h :: t) => head.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + head.length, t) }
)
}

Expand All @@ -26,21 +28,24 @@ trait WritePlatform extends LowerPriorityWrite {
trait LowerPriorityWrite extends EvenLowerPriorityWrite {

implicit def product[H, T <: HList](
implicit H: Lazy[Write[H]],
T: Lazy[Write[T]]
): Write[H :: T] =
new Write(
H.value.puts ++ T.value.puts,
{ case h :: t => H.value.toList(h) ++ T.value.toList(t) },
{ case (ps, n, h :: t) => H.value.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + H.value.length, t) },
{ case (rs, n, h :: t) => H.value.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + H.value.length, t) }
implicit H: Lazy[Write[H] OrElse MkWrite[H]],
T: Lazy[MkWrite[T]]
): MkWrite[H :: T] = {
val head = H.value.unify

new MkWrite(
head.puts ++ T.value.puts,
{ case h :: t => head.toList(h) ++ T.value.toList(t) },
{ case (ps, n, h :: t) => head.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + head.length, t) },
{ case (rs, n, h :: t) => head.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + head.length, t) }
)
}

implicit def emptyProduct: Write[HNil] =
new Write[HNil](Nil, _ => Nil, (_, _, _) => (), (_, _, _) => ())
implicit val emptyProduct: MkWrite[HNil] =
new MkWrite[HNil](Nil, _ => Nil, (_, _, _) => (), (_, _, _) => ())

implicit def generic[B, A](implicit gen: Generic.Aux[B, A], A: Lazy[Write[A]]): Write[B] =
new Write[B](
implicit def generic[B, A](implicit gen: Generic.Aux[B, A], A: Lazy[MkWrite[A]]): MkWrite[B] =
new MkWrite[B](
A.value.puts,
b => A.value.toList(gen.to(b)),
(ps, n, b) => A.value.unsafeSet(ps, n, gen.to(b)),
Expand All @@ -51,50 +56,52 @@ trait LowerPriorityWrite extends EvenLowerPriorityWrite {

trait EvenLowerPriorityWrite {

implicit val ohnil: Write[Option[HNil]] =
new Write[Option[HNil]](Nil, _ => Nil, (_, _, _) => (), (_, _, _) => ())
implicit val ohnil: MkWrite[Option[HNil]] =
new MkWrite[Option[HNil]](Nil, _ => Nil, (_, _, _) => (), (_, _, _) => ())

implicit def ohcons1[H, T <: HList](
implicit H: Lazy[Write[Option[H]]],
T: Lazy[Write[Option[T]]],
implicit H: Lazy[Write[Option[H]] OrElse MkWrite[Option[H]]],
T: Lazy[MkWrite[Option[T]]],
N: H <:!< Option[α] forSome { type α }
): Write[Option[H :: T]] = {
): MkWrite[Option[H :: T]] = {
void(N)
val head = H.value.unify

def split[A](i: Option[H :: T])(f: (Option[H], Option[T]) => A): A =
i.fold(f(None, None)) { case h :: t => f(Some(h), Some(t)) }

new Write(
H.value.puts ++ T.value.puts,
split(_) { (h, t) => H.value.toList(h) ++ T.value.toList(t) },
(ps, n, i) => split(i) { (h, t) => H.value.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + H.value.length, t) },
(rs, n, i) => split(i) { (h, t) => H.value.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + H.value.length, t) }
new MkWrite(
head.puts ++ T.value.puts,
split(_) { (h, t) => head.toList(h) ++ T.value.toList(t) },
(ps, n, i) => split(i) { (h, t) => head.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + head.length, t) },
(rs, n, i) => split(i) { (h, t) => head.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + head.length, t) }
)

}

implicit def ohcons2[H, T <: HList](
implicit H: Lazy[Write[Option[H]]],
T: Lazy[Write[Option[T]]]
): Write[Option[Option[H] :: T]] = {
implicit H: Lazy[Write[Option[H]] OrElse MkWrite[Option[H]]],
T: Lazy[MkWrite[Option[T]]]
): MkWrite[Option[Option[H] :: T]] = {
val head = H.value.unify

def split[A](i: Option[Option[H] :: T])(f: (Option[H], Option[T]) => A): A =
i.fold(f(None, None)) { case oh :: t => f(oh, Some(t)) }

new Write(
H.value.puts ++ T.value.puts,
split(_) { (h, t) => H.value.toList(h) ++ T.value.toList(t) },
(ps, n, i) => split(i) { (h, t) => H.value.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + H.value.length, t) },
(rs, n, i) => split(i) { (h, t) => H.value.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + H.value.length, t) }
new MkWrite(
head.puts ++ T.value.puts,
split(_) { (h, t) => head.toList(h) ++ T.value.toList(t) },
(ps, n, i) => split(i) { (h, t) => head.unsafeSet(ps, n, h); T.value.unsafeSet(ps, n + head.length, t) },
(rs, n, i) => split(i) { (h, t) => head.unsafeUpdate(rs, n, h); T.value.unsafeUpdate(rs, n + head.length, t) }
)

}

implicit def ogeneric[B, A <: HList](
implicit G: Generic.Aux[B, A],
A: Lazy[Write[Option[A]]]
): Write[Option[B]] =
new Write(
A: Lazy[MkWrite[Option[A]]]
): MkWrite[Option[B]] =
new MkWrite(
A.value.puts,
b => A.value.toList(b.map(G.to)),
(rs, n, a) => A.value.unsafeSet(rs, n, a.map(G.to)),
Expand Down
6 changes: 4 additions & 2 deletions modules/core/src/main/scala-3/util/GetPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ trait GetPlatform:
using p: Mirror.ProductOf[P],
i: p.MirroredElemTypes =:= (A *: EmptyTuple),
g: Get[A],
): Get[P] =
g.map(a => p.fromProduct(a *: EmptyTuple))
): MkGet[P] = {
val get = g.map(a => p.fromProduct(a *: EmptyTuple))
MkGet.lift(get)
}
6 changes: 4 additions & 2 deletions modules/core/src/main/scala-3/util/PutPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ trait PutPlatform:
using m: Mirror.ProductOf[P],
i: m.MirroredElemTypes =:= (A *: EmptyTuple),
p: Put[A]
): Put[P] =
p.contramap(p => i(Tuple.fromProductTyped(p)).head)
): MkPut[P] = {
val put: Put[P] = p.contramap(p => i(Tuple.fromProductTyped(p)).head)
MkPut.lift(put)
}
Loading

0 comments on commit 8b9058b

Please sign in to comment.