Skip to content

Commit

Permalink
opt
Browse files Browse the repository at this point in the history
  • Loading branch information
ValdemarGr committed Feb 21, 2024
1 parent f75847e commit 2cb7780
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ class QueryPreparation[F[_], C](
prepareStep[I2, O2](step, fieldMeta, uec append edge)

step match {
case Step.Alg.Lift(f) => liftK(nextNodeId).map(PreparedStep.Lift(_, f))
case Step.Alg.EmbedError() => liftK(nextNodeId).map(PreparedStep.EmbedError[F, O](_))
case _: Step.Alg.Identity[I] => liftK(nextNodeId).map(PreparedStep.Lift(_, identity))
case Step.Alg.Lift(f) => liftK(nextNodeId).map(PreparedStep.Lift(_, f))
case Step.Alg.EmbedError() => liftK(nextNodeId).map(PreparedStep.EmbedError[F, O](_))
case alg: Step.Alg.Compose[F, i, a, o] =>
val left = rec[i, a](alg.left, "compose-left")
val right = rec[a, o](alg.right, "compose-right")
Expand Down
6 changes: 5 additions & 1 deletion modules/core/src/main/scala/gql/resolver/Resolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ final class Resolver[+F[_], -I, +O](private[gql] val underlying: Step[F, I, O])
def step: Step[F, I, O] = underlying

def covaryAll[F2[x] >: F[x], O2 >: O]: Resolver[F2, I, O2] = this

def optimize: Resolver[F, I, O] = new Resolver(Step.optimize(underlying))
}

object Resolver extends ResolverInstances {
Expand All @@ -80,7 +82,7 @@ object Resolver extends ResolverInstances {
def lift[F[_], I]: PartiallyAppliedLift[F, I] = new PartiallyAppliedLift[F, I]

def id[F[_], I]: Resolver[F, I, I] =
lift(identity)
new Resolver(Step.identity)

def effectFull[F[_], I, O](f: I => F[O]): Resolver[F, I, O] =
liftFull(f).andThen(new Resolver(Step.embedEffect))
Expand Down Expand Up @@ -210,5 +212,7 @@ trait ResolverInstances {
new Resolver(Step.first(fa.underlying))

override def lift[A, B](f: A => B): Resolver[F, A, B] = Resolver.lift(f)

override def id[A]: Resolver[F, A, A] = Resolver.id
}
}
130 changes: 119 additions & 11 deletions modules/core/src/main/scala/gql/resolver/Step.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,73 @@ package gql.resolver

import gql._
import cats.data._
import cats.implicits._
import cats._

/** A step is a composable task that takes an input and produces an output.
*/
sealed trait Step[+F[_], -I, +O]

object Step {
object Alg {
final case class Lift[I, O](f: I => O) extends AnyRef with Step[Nothing, I, O]
final case class Identity[I]() extends AnyRef with Step[Nothing, I, I] {
override def toString: String = s"Identity"
}

final case class Lift[I, O](f: I => O) extends AnyRef with Step[Nothing, I, O] {
override def toString: String = s"Lift(...)"
}

final case class EmbedEffect[F[_], I]() extends AnyRef with Step[F, F[I], I]
final case class EmbedEffect[F[_], I]() extends AnyRef with Step[F, F[I], I] {
override def toString: String = s"EmbedEffect"
}

final case class EmbedStream[F[_], I](
signal: Boolean
) extends AnyRef
with Step[F, fs2.Stream[F, I], I]
with Step[F, fs2.Stream[F, I], I] {
override def toString: String = s"EmbedStream(signal=$signal)"
}

final case class EmbedError[I]() extends AnyRef with Step[Nothing, Ior[String, I], I]
final case class EmbedError[I]() extends AnyRef with Step[Nothing, Ior[String, I], I] {
override def toString: String = s"EmbedError"
}

final case class Argument[I, A](arg: Arg[A]) extends Step[Nothing, I, A]
final case class Argument[I, A](arg: Arg[A]) extends Step[Nothing, I, A] {
override def toString: String = s"Argument(${arg.entries.map(_.name).mkString_(", ")})"
}

final case class Compose[F[_], I, A, O](left: Step[F, I, A], right: Step[F, A, O]) extends Step[F, I, O]
final case class Compose[F[_], I, A, O](left: Step[F, I, A], right: Step[F, A, O]) extends Step[F, I, O] {
override def toString: String = s"Compose($left, $right)"
}

final case class Choose[F[_], A, B, C, D](fac: Step[F, A, C], fab: Step[F, B, D]) extends Step[F, Either[A, B], Either[C, D]]
final case class Choose[F[_], A, B, C, D](
fac: Step[F, A, C],
fab: Step[F, B, D]
) extends Step[F, Either[A, B], Either[C, D]] {
override def toString: String = s"Choose($fac, $fab)"
}

final case class GetMeta[F[_], I]() extends Step[Nothing, I, FieldMeta[F]]
final case class GetMeta[F[_], I]() extends Step[Nothing, I, FieldMeta[F]] {
override def toString: String = s"GetMeta"
}

final case class First[F[_], A, B, C](step: Step[F, A, B]) extends Step[F, (A, C), (B, C)]
final case class First[F[_], A, B, C](step: Step[F, A, B]) extends Step[F, (A, C), (B, C)] {
override def toString: String = s"First($step)"
}

final case class Batch[F[_], K, V](id: BatchKey[K, V]) extends Step[F, Set[K], Map[K, V]]
final case class Batch[F[_], K, V](id: BatchKey[K, V]) extends Step[F, Set[K], Map[K, V]] {
override def toString: String = s"Batch($id)"
}

final case class InlineBatch[F[_], K, V](run: Set[K] => F[Map[K, V]]) extends Step[F, Set[K], Map[K, V]]
final case class InlineBatch[F[_], K, V](run: Set[K] => F[Map[K, V]]) extends Step[F, Set[K], Map[K, V]] {
override def toString: String = s"InlineBatch"
}
}

def identity[I]: Step[Nothing, I, I] =
Alg.Identity()

def lift[F[_], I, O](f: I => O): Step[F, I, O] =
Alg.Lift(f)

Expand Down Expand Up @@ -92,6 +126,77 @@ object Step {
def inlineBatch[F[_], K, V](f: Set[K] => F[Map[K, V]]): Step[F, Set[K], Map[K, V]] =
Alg.InlineBatch(f)

def optimize[F[_], I, O](step: Step[F, I, O]): Step[F, I, O] = {
sealed trait CurrentContinuation[A, B] {
def apply[A0](s: Step[F, A0, A]): Step[F, A0, B]
def push[C](f: Step[F, C, A]): CurrentContinuation[C, B] =
CurrentContinuation.CC(f, this)
}
object CurrentContinuation {
final case class Identity[A]() extends CurrentContinuation[A, A] {
def apply[A0](s: Step[F, A0, A]): Step[F, A0, A] = s
}
final case class CC[A, B, C](
l: Step[F, A, B],
r: CurrentContinuation[B, C]
) extends CurrentContinuation[A, C] {
def apply[A0](s: Step[F, A0, A]): Step[F, A0, C] =
r(Step.Alg.Compose(s, l))
}
}

def optimizeCompose[A1, B1](s: Step[F, A1, B1]): Step[F, A1, B1] = s match {
case alg: Alg.Compose[F, A1, a, B1] =>
type A = a
val e = (alg.left, alg.right) match {
case (_: Alg.Identity[i], r) => r.some
case (l, _: Alg.Identity[o]) => l.some
case (l: Alg.Lift[i, A], r: Alg.Lift[A, o]) => Alg.Lift(r.f.compose(l.f)).some
case (l: Alg.Lift[i, A], rc: Alg.Compose[F, A, a2, o]) =>
rc.left match {
case rl: Alg.Lift[A, o] => Alg.Compose(Alg.Lift(rl.f.compose(l.f)), rc.right).some
case _ => None
}
case _ => None
}
e.map(_.asInstanceOf[Step[F, A1, B1]]).getOrElse(alg)
case _ => s
}

def aux[A0, B0, C0](
step: Step[F, A0, B0],
cc: CurrentContinuation[B0, C0]
): Eval[Step[F, A0, C0]] = Eval.defer[Step[F, A0, C0]] {
step match {
case alg: Alg.Compose[F, i, a, o] =>
aux(alg.right, cc).flatMap { r =>
aux(alg.left, CurrentContinuation.CC(r, CurrentContinuation.Identity[C0]())).map(optimizeCompose)
}
case alg: Alg.Choose[F, a, b, c, d] =>
(
aux(alg.fac, CurrentContinuation.Identity[c]()),
aux(alg.fab, CurrentContinuation.Identity[d]())
).mapN(Alg.Choose[F, a, b, c, d](_, _)).map(x => optimizeCompose(cc(x)))

case alg: Alg.First[F, a, b, c] =>
type A = a
type B = b
aux(alg.step, CurrentContinuation.Identity[b]())
.map { x =>
val e = x match {
case _: Alg.Identity[?] => Alg.Identity[(A, c)]().some
case alg: Alg.Lift[A, B] => Alg.Lift[(A, c), (B, c)] { case (a, c) => (alg.f(a), c) }.some
case _ => None
}
e.map(_.asInstanceOf[Step[F, (A, c), (B, c)]]).getOrElse(Alg.First[F, a, b, c](x))
}
.map(x => optimizeCompose(cc(x)))
case _ => Eval.now(optimizeCompose(cc(step)))
}
}
aux(step, CurrentContinuation.Identity[O]()).value
}

import cats.arrow._
implicit def arrowChoiceForStep[F[_]]: ArrowChoice[Step[F, *, *]] = new ArrowChoice[Step[F, *, *]] {
override def choose[A, B, C, D](f: Step[F, A, C])(g: Step[F, B, D]): Step[F, Either[A, B], Either[C, D]] =
Expand All @@ -102,5 +207,8 @@ object Step {
override def first[A, B, C](fa: Step[F, A, B]): Step[F, (A, C), (B, C)] = Step.first(fa)

override def lift[A, B](f: A => B): Step[F, A, B] = Step.lift(f)

// optimization
override def id[A]: Step[F, A, A] = Step.identity
}
}
2 changes: 1 addition & 1 deletion modules/server/src/test/scala/gql/SkipTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class SkipTest extends CatsEffectSuite {
case None => IO(Left(20))
case Some(i) => IO.pure(Right(i))
}
}.leftThrough(_.evalMap(i => effectState.modify(_ => (Some(i), i)))).map(_.merge)
}.leftThrough(_.evalMap(i => effectState.modify(_ => (Some(i), i)))).map(_.merge).optimize
)
)
}
Expand Down

0 comments on commit 2cb7780

Please sign in to comment.