diff --git a/build.sbt b/build.sbt index fd4f97262..d3556354f 100644 --- a/build.sbt +++ b/build.sbt @@ -139,6 +139,12 @@ lazy val core = project .settings(sharedSettings) .settings(name := "gql-core") .dependsOn(parser) + +lazy val monadicArrow = project + .in(file("modules/monadic-arrow")) + .settings(sharedSettings) + .settings(name := "gql-monadic-arrow") + .dependsOn(core) lazy val server = project .in(file("modules/server")) diff --git a/modules/core/src/main/scala/gql/Modifier.scala b/modules/core/src/main/scala/gql/Modifier.scala index 3c2f85f1c..96473c2cb 100644 --- a/modules/core/src/main/scala/gql/Modifier.scala +++ b/modules/core/src/main/scala/gql/Modifier.scala @@ -16,7 +16,6 @@ package gql import gql.ast._ -import gql.resolver.Resolver sealed trait Modifier object Modifier { @@ -163,370 +162,4 @@ object InverseModifierStack { } case Nil => ms.inner } -} - -import cats.implicits._ -import cats.free._ -import cats.arrow._ -import cats._ -import cats.data._ -import org.tpolecat.sourcepos._ - -object Attempt3 { - final case class FetchVar[A]( - id: Int, - pos: Option[SourcePos], - compilerPos: SourcePos - ) - type Var[A] = FreeApplicative[FetchVar, A] - - class Language[Arrow0[_, _]] { - sealed trait Declaration[A] - object Declaration { - case class Declare[A, B](v: Var[A], arrow: Arrow0[A, B], pos: SourcePos) extends Declaration[Var[B]] - } - - final type Decl[A] = Free[Declaration, A] - - def declare[A, B](v: Var[A])(f: Arrow0[A, B])(implicit sp: SourcePos): Decl[Var[B]] = - Free.liftF[Declaration, Var[B]](Declaration.Declare(v, f, sp)) - - def compileFull[A, B](f: Var[A] => Decl[Var[B]])(implicit arrow: Arrow[Arrow0], sp: SourcePos): Arrow0[A, B] = { - val init = FreeApplicative.lift(FetchVar[A](0, None, sp)) - val program = f(init) - type U = Vector[Any] - type S = (Int, Arrow0[U, U]) - type G[C] = State[S, C] - - val nextId: G[Int] = State[S, Int] { case (x, u) => ((x + 1, u), x) } - - def varCompiler(x: U) = new (FetchVar ~> Id) { - def apply[A0](fa: FetchVar[A0]): Id[A0] = - if (fa.compilerPos eq sp) x(fa.id).asInstanceOf[A0] - else { - val msg = fa.pos match { - case None => - s"""|Initial variable introduced at ${fa.compilerPos}. - |Variables that were not declared in this scope may not be referenced. - |Example: - |``` - |compile[Int]{ init => - | for { - | y <- init.apply(_.andThen(compile[Int]{ _ => - | // referencing 'init' here is an error - | init.apply(_.map(_ + 1)) - | })) - | } yield y - |} - |```""".stripMargin - case Some(p) => - s"""|Variable declared at ${p}. - |Compilation initiated at ${fa.compilerPos}. - |Variables that were not declared in this scope may not be referenced. - |Example: - |``` - |compile[Int]{ init => - | for { - | x <- init.apply(_.map(_ + 1)) - | y <- init.apply(_.andThen(compile[Int]{ _ => - | // referencing 'x' here is an error - | x.apply(_.map(_ + 1)) - | })) - | } yield y - |} - |```""".stripMargin - } - throw new RuntimeException( - s"""|Variable closure error. - |$msg""".stripMargin - ) - } - } - - val arrowCompiler = new (Declaration ~> G) { - def apply[A1](fa: Declaration[A1]): G[A1] = fa match { - case alg: Declaration.Declare[a, b] => - nextId.flatMap { thisId => - val fetchVar = FetchVar[b](thisId, alg.pos.some, sp) - val thisArr: Arrow0[U, U] = alg.arrow - .first[U] - .lmap[U](m => (alg.v.foldMap(varCompiler(m)), m)) - .rmap { case (b, u) => u.updated(fetchVar.id, b) } - - State.modify[S] { case (x, arr) => (x, arr >>> thisArr) }.as(FreeApplicative.lift(fetchVar)) - } - } - } - - val ((varNum, arrowProgram), lastVar) = program - .foldMap(arrowCompiler) - .run((1, Arrow[Arrow0].lift[U, U](identity))) - .value - - val b = Vector.fill[Any](varNum)(null) - arrowProgram - .map(u => lastVar.foldMap(varCompiler(u))) - .lmap[A] { a => - b.updated(0, a) - } - } - } - - abstract class LanguageDsl[Arrow0[_, _]: Arrow] extends Language[Arrow0] { self => - def liftArrow[A](f: Arrow0[Unit, Unit] => Arrow0[Unit, A])(implicit sp: SourcePos): Decl[Var[A]] = - declare[Unit, A](().pure[Var])(f(Arrow[Arrow0].id[Unit])) - - implicit class VarOps[A](v: Var[A]) { - def declare[B](f: Arrow0[A, B])(implicit sp: SourcePos): Decl[Var[B]] = self.declare(v)(f) - - def apply[B](f: Arrow0[A, A] => Arrow0[A, B])(implicit sp: SourcePos): Decl[Var[B]] = - declare(f(Arrow[Arrow0].id[A])) - } - - final class PartiallyAppliedLanguageCompiler[A](private val dummy: Boolean = true) { - def apply[B](f: Var[A] => Decl[Var[B]])(implicit sp: SourcePos): Arrow0[A, B] = compileFull(f) - } - - def compile[A]: PartiallyAppliedLanguageCompiler[A] = new PartiallyAppliedLanguageCompiler[A] - } - - object GQLL { - trait ResolverDsl[F[_]] extends LanguageDsl[Resolver[F, *, *]] { - def argument[A](arg: Arg[A])(implicit sp: SourcePos): Decl[Var[A]] = - liftArrow(_.andThen(Resolver.argument[F, Unit, A](arg))) - } - object ResolverDsl { - def apply[F[_]]: ResolverDsl[F] = new ResolverDsl[F] {} - } - - import cats.effect._ - val d = ResolverDsl[IO] - import d._ - import gql.dsl.all._ - val res = compile[Int] { init => - for { - y <- init.apply(_.evalMap(x => IO(x * 2))) - z <- argument(arg[Int]("gab")) - h <- (y, z).tupled.apply(_.map { case (y0, z0) => y0 + z0 }) - } yield h - } - } - -} -/* -object Attempt2 { - sealed trait ArrowAlg[Arrow0[_, _], A, V[_]] - object ArrowAlg { - case class Declare[Arrow0[_, _], A, B, V[_]]( - v: V[A], - arrow: Arrow0[A, B] - ) extends ArrowAlg[Arrow0, V[B], V] - } - final type Declaration[Arrow0[_, _], A, V[_]] = Free[ArrowAlg[Arrow0, *, V], A] - - trait LangBase { - case class FetchVar[X](id: Int) - final type Var[A] = FreeApplicative[FetchVar, A] - - def compile[Arrow0[_, _]: Arrow, A, B](f: Var[A] => Declaration[Arrow0, Var[B], Var]): Arrow0[A, B] - } - object LangBase { - def apply: LangBase = new LangBase { - override def compile[Arrow0[_, _]: Arrow, A, B]( - f: Var[A] => Declaration[Arrow0, Var[B], Var] - ): Arrow0[A, B] = { - type U = Array[Any] - type S = (Int, Arrow0[U, U]) - type G[C] = State[S, C] - type AA[C] = ArrowAlg[Arrow0, C, Var] - - def nextId: G[Int] = State[S, Int] { case (x, u) => ((x + 1, u), x) } - - val init = FreeApplicative.lift(FetchVar[A](0)) - val program = f(init) - - def varCompiler(x: U) = new (FetchVar ~> Id) { - def apply[A0](fa: FetchVar[A0]): Id[A0] = x(fa.id).asInstanceOf[A0] - } - val arrowCompiler = new (AA ~> G) { - def apply[A1](fa: AA[A1]): G[A1] = fa match { - case alg: ArrowAlg.Declare[Arrow0, a, b, Var] => - nextId.flatMap { thisId => - val fetchVar = FetchVar[b](thisId) - val thisArr: Arrow0[U, U] = alg.arrow - .first[U] - .lmap[U](m => (alg.v.foldMap(varCompiler(m)), m)) - .rmap { case (b, u) => u.update(fetchVar.id, b); u } - - State.modify[S] { case (x, arr) => (x, arr >>> thisArr) }.as(FreeApplicative.lift(fetchVar)) - } - } - } - - val ((varNum, arrowProgram), lastVar) = program - .foldMap(arrowCompiler) - .run((1, Arrow[Arrow0].lift[U, U](identity))) - .value - - arrowProgram - .map(u => lastVar.foldMap(varCompiler(u))) - .lmap[A] { a => - val arr = Array.ofDim[Any](varNum) - arr.update(0, a) - arr - } - } - } - } - - def declare[Arrow0[_, _], A, B](language: LangBase)(v: language.Var[A])( - arrow: Arrow0[A, B] - ): Declaration[Arrow0, language.Var[B], language.Var] = - Free.liftF[ArrowAlg[Arrow0, *, language.Var], language.Var[B]]( - ArrowAlg.Declare(v, arrow) - ) - - trait Lang[Arrow0[_, _], A] { - case class FetchVar[X](id: Int) - sealed trait ArrowAlg[X] - final object ArrowAlg { - case class Declare[Y, X]( - v: Var[Y], - arrow: Arrow0[Y, X] - ) extends ArrowAlg[Var[X]] - } - type Var[X] = FreeApplicative[FetchVar, X] - final type Declaration[X] = Free[ArrowAlg, X] - - def init: Var[A] = FreeApplicative.lift(FetchVar(0)) - - def compile[B](program: Declaration[Var[B]])(implicit A: Arrow[Arrow0]): Arrow0[A, B] - - def declare[B, C](v: Var[B])(f: Arrow0[B, C]): Declaration[Var[C]] - } - - object Lang { - def apply[Arrow0[_, _], A]: Lang[Arrow0, A] = - new Lang[Arrow0, A] { - override def init: FreeApplicative[FetchVar, A] = FreeApplicative.lift(FetchVar(0)) - override def declare[B, C](v: Var[B])(f: Arrow0[B, C]): Declaration[Var[C]] = - Free.liftF[ArrowAlg, Var[C]](ArrowAlg.Declare(v, f)) - override def compile[B](program: Declaration[Var[B]])(implicit A: Arrow[Arrow0]): Arrow0[A, B] = { - type U = Array[Any] - type S = (Int, Arrow0[U, U]) - type G[C] = State[S, C] - - def nextId: G[Int] = State[S, Int] { case (x, u) => ((x + 1, u), x) } - - scala.collection.immutable.HashMap - - def varCompiler(x: U) = new (FetchVar ~> Id) { - def apply[A0](fa: FetchVar[A0]): Id[A0] = x(fa.id).asInstanceOf[A0] - } - val arrowCompiler = new (ArrowAlg ~> G) { - def apply[A1](fa: ArrowAlg[A1]): G[A1] = fa match { - case alg: ArrowAlg.Declare[a, b] => - nextId.flatMap { thisId => - val fetchVar = FetchVar[b](thisId) - val thisArr: Arrow0[U, U] = alg.arrow - .first[U] - .lmap[U](m => (alg.v.foldMap(varCompiler(m)), m)) - .rmap { case (b, u) => u.update(fetchVar.id, b); u } - - State.modify[S] { case (x, arr) => (x, arr >>> thisArr) }.as(FreeApplicative.lift(fetchVar)) - } - } - } - - val ((varNum, arrowProgram), lastVar) = program - .foldMap(arrowCompiler) - .run((1, Arrow[Arrow0].lift[U, U](identity))) - .value - - arrowProgram - .map(u => lastVar.foldMap(varCompiler(u))) - .lmap[A] { a => - val arr = Array.ofDim[Any](varNum) - arr.update(0, a) - arr - } - } - } - } - -/* - final class PartiallyAppliedCompiler[A] { - def apply[Arrow0[_, _]: Arrow, B](f: Var[A] => FreeArrow[Arrow0, Var[B]]): Arrow0[A, B] = - // Compiler.compileFull(null) - } - - def compile[A]: PartiallyAppliedCompiler[A] = new PartiallyAppliedCompiler[A]*/ -/* - object Test { - case class MyArrow[A, B](f: A => B) - implicit lazy val arrowForMyArrow: Arrow[MyArrow] = - new Arrow[MyArrow] { - override def compose[A, B, C](f: MyArrow[B, C], g: MyArrow[A, B]): MyArrow[A, C] = - MyArrow(f.f.compose(g.f)) - - override def first[A, B, C](fa: MyArrow[A, B]): MyArrow[(A, C), (B, C)] = - MyArrow { case (a, c) => (fa.f(a), c) } - - override def lift[A, B](f: A => B): MyArrow[A, B] = - MyArrow(f) - } - - object MyArrowDsl extends Dsl[MyArrow] - import MyArrowDsl._ - - val result: MyArrow[Int, String] = Lang[MyArrow, Int] { x => - for { - x <- x.init.decl(_.map(_ - 2).map(_ + 2).map(_ - 2)) - y <- init.decl(_.map(_ * 2)) - k <- (x, x, x).tupled.decl(_.map { case (x1, x2, x3) => x1 + x2 + x3 }) - z <- (k, y).tupled.decl(_.map { case (k, y) => k + y }.map(_.toString)) - } yield z - } - } - - object Practical { - import cats.effect._ - object ResolverDsl extends Dsl[Resolver[IO, *, *]] - import ResolverDsl._ - import Resolver._ - - import gql.dsl.all._ - - cats.syntax.arrow - val ageArg = arg[Int]("age") - val result: Resolver[IO, Int, String] = compile { init => - for { - x <- init.decl(_.evalMap(x => IO(x - 2)).evalMap(x => IO(x + 2)).evalMap(x => IO(x - 2))) - y <- init.decl(_.streamMap(i => fs2.Stream(i))) - age <- init.decl(_ *> argument(ageArg)) - z <- (x, y, age).tupled.decl(_.map { case (x, y, age) => x + y + age }.map(_.toString)) - } yield z - } - }*/ - - type ArrowIO[A, B] = A => B - val ArrowForMyIO = Arrow[ArrowIO] - final case class Variable[A](id: Int) - type Var[A] = FreeApplicative[Variable, A] - sealed trait Algebra[A] - case class Declare[A, B](v: Var[A], arrow: ArrowIO[A, B]) extends Algebra[Var[B]] - type MyIO[A] = Free[Algebra, A] - - def delay[A](f: => A): MyIO[Var[A]] = - Free.liftF[Algebra, Var[A]](Declare[Unit, A](().pure[Var], _ => f)) - - def derive[A, B](v: Var[A])(f: ArrowIO[A, B]): MyIO[Var[B]] = - Free.liftF[Algebra, Var[B]](Declare(v, f)) - - for { - x <- delay(1 + 1) - y <- derive(x)(_ * 2) - z <- derive((x,y).tupled){ case (a, b) => a + b } - } yield z -} - */ +} \ No newline at end of file diff --git a/modules/monadic-arrow/src/main/scala/gql/arrow/Language.scala b/modules/monadic-arrow/src/main/scala/gql/arrow/Language.scala new file mode 100644 index 000000000..edab8e64d --- /dev/null +++ b/modules/monadic-arrow/src/main/scala/gql/arrow/Language.scala @@ -0,0 +1,126 @@ +package gql.arrow + +import cats.implicits._ +import cats.free._ +import cats.arrow._ +import cats._ +import cats.data._ +import org.tpolecat.sourcepos._ + +final case class FetchVar[A]( + id: Int, + pos: Option[SourcePos], + compilerPos: SourcePos +) + +class Language[Arrow0[_, _]] { + final type Var[A] = FreeApplicative[FetchVar, A] + + sealed trait Declaration[A] + object Declaration { + case class Declare[A, B](v: Var[A], arrow: Arrow0[A, B], pos: SourcePos) extends Declaration[Var[B]] + } + + final type Decl[A] = Free[Declaration, A] + + def declare[A, B](v: Var[A])(f: Arrow0[A, B])(implicit sp: SourcePos): Decl[Var[B]] = + Free.liftF[Declaration, Var[B]](Declaration.Declare(v, f, sp)) + + def compileFull[A, B](f: Var[A] => Decl[Var[B]])(implicit arrow: Arrow[Arrow0], sp: SourcePos): Arrow0[A, B] = { + val init = FreeApplicative.lift(FetchVar[A](0, None, sp)) + val program = f(init) + type U = Vector[Any] + type S = (Int, Arrow0[U, U]) + type G[C] = State[S, C] + + val nextId: G[Int] = State[S, Int] { case (x, u) => ((x + 1, u), x) } + + def varCompiler(x: U) = new (FetchVar ~> Id) { + def apply[A0](fa: FetchVar[A0]): Id[A0] = + if (fa.compilerPos eq sp) x(fa.id).asInstanceOf[A0] + else { + val msg = fa.pos match { + case None => + s"""|Initial variable introduced at ${fa.compilerPos}. + |Variables that were not declared in this scope may not be referenced. + |Example: + |``` + |compile[Int]{ init => + | for { + | y <- init.apply(_.andThen(compile[Int]{ _ => + | // referencing 'init' here is an error + | init.apply(_.map(_ + 1)) + | })) + | } yield y + |} + |```""".stripMargin + case Some(p) => + s"""|Variable declared at ${p}. + |Compilation initiated at ${fa.compilerPos}. + |Variables that were not declared in this scope may not be referenced. + |Example: + |``` + |compile[Int]{ init => + | for { + | x <- init.apply(_.map(_ + 1)) + | y <- init.apply(_.andThen(compile[Int]{ _ => + | // referencing 'x' here is an error + | x.apply(_.map(_ + 1)) + | })) + | } yield y + |} + |```""".stripMargin + } + throw new RuntimeException( + s"""|Variable closure error. + |$msg""".stripMargin + ) + } + } + + val arrowCompiler = new (Declaration ~> G) { + def apply[A1](fa: Declaration[A1]): G[A1] = fa match { + case alg: Declaration.Declare[a, b] => + nextId.flatMap { thisId => + val fetchVar = FetchVar[b](thisId, alg.pos.some, sp) + val thisArr: Arrow0[U, U] = alg.arrow + .first[U] + .lmap[U](m => (alg.v.foldMap(varCompiler(m)), m)) + .rmap { case (b, u) => u.updated(fetchVar.id, b) } + + State.modify[S] { case (x, arr) => (x, arr >>> thisArr) }.as(FreeApplicative.lift(fetchVar)) + } + } + } + + val ((varNum, arrowProgram), lastVar) = program + .foldMap(arrowCompiler) + .run((1, Arrow[Arrow0].lift[U, U](identity))) + .value + + val b = Vector.fill[Any](varNum)(null) + arrowProgram + .map(u => lastVar.foldMap(varCompiler(u))) + .lmap[A] { a => + b.updated(0, a) + } + } +} + +abstract class LanguageDsl[Arrow0[_, _]: Arrow] extends Language[Arrow0] { self => + def liftArrow[A](f: Arrow0[Unit, Unit] => Arrow0[Unit, A])(implicit sp: SourcePos): Decl[Var[A]] = + declare[Unit, A](().pure[Var])(f(Arrow[Arrow0].id[Unit])) + + implicit class VarOps[A](v: Var[A]) { + def declare[B](f: Arrow0[A, B])(implicit sp: SourcePos): Decl[Var[B]] = self.declare(v)(f) + + def apply[B](f: Arrow0[A, A] => Arrow0[A, B])(implicit sp: SourcePos): Decl[Var[B]] = + declare(f(Arrow[Arrow0].id[A])) + } + + final class PartiallyAppliedLanguageCompiler[A](private val dummy: Boolean = true) { + def apply[B](f: Var[A] => Decl[Var[B]])(implicit sp: SourcePos): Arrow0[A, B] = compileFull(f) + } + + def compile[A]: PartiallyAppliedLanguageCompiler[A] = new PartiallyAppliedLanguageCompiler[A] +} diff --git a/modules/monadic-arrow/src/main/scala/gql/arrow/dsl.scala b/modules/monadic-arrow/src/main/scala/gql/arrow/dsl.scala new file mode 100644 index 000000000..3bed932bc --- /dev/null +++ b/modules/monadic-arrow/src/main/scala/gql/arrow/dsl.scala @@ -0,0 +1,14 @@ +package gql.arrow + +import gql.resolver._ +import gql._ +import org.tpolecat.sourcepos.SourcePos + +trait dsl[F[_]] extends LanguageDsl[Resolver[F, *, *]] { + def argument[A](arg: Arg[A])(implicit sp: SourcePos): Decl[Var[A]] = + liftArrow(_.andThen(Resolver.argument[F, Unit, A](arg))) +} + +object dsl { + def apply[F[_]]: dsl[F] = new dsl[F] {} +} diff --git a/modules/monadic-arrow/src/test/scala/gql/arrow/ArrowTest.scala b/modules/monadic-arrow/src/test/scala/gql/arrow/ArrowTest.scala new file mode 100644 index 000000000..92fc81b2c --- /dev/null +++ b/modules/monadic-arrow/src/test/scala/gql/arrow/ArrowTest.scala @@ -0,0 +1,9 @@ +package gql.arrow + +import munit.CatsEffectSuite + +class ArrowTest extends CatsEffectSuite { + test("arrow compilation should work properly") { + + } +}