package zio.http.api.internal sealed trait Box[A] extends Product with Serializable { self => def zip[B](that: Box[B])(implicit zipper: Zipper[A, B]): Box[zipper.Out] = Box.Zip[A, B, zipper.Out](self, that, zipper) def map[B](f: A => B)(implicit tupleSize: TupleSize[B]): Box[B] = Box.Map(self, f, tupleSize) def tupleSize: Int = self match { case Box.Zip(left, right, _) => left.tupleSize + right.tupleSize case Box.Map(_, _, tupleSize) => tupleSize.size case Box.Succeed(_, tupleSize) => tupleSize.size } } object Box { def succeed[A](value: A)(implicit size: TupleSize[A]): Box[A] = Succeed(value, size) final case class Succeed[A](value: A, size: TupleSize[A]) extends Box[A] final case class Zip[A, B, C](lhs: Box[A], rhs: Box[B], zipper: Zipper.WithOut[A, B, C]) extends Box[C] final case class Map[A, B](box: Box[A], f: A => B, size: TupleSize[B]) extends Box[B] def makeConstructor[A](box: Box[A]): Array[Any] => A = { box match { case Succeed(value, _) => _ => value case zip @ Zip(_, _, _) => makeZipper(zip) case Map(box, f, _) => val constructor = makeConstructor(box) args => f(constructor(args)) } } private def makeZipper[A](box: Zip[_, _, A]): Array[Any] => A = { if (countNestedZips(box) <= 2) { // If there aren't enough nested zips, this optimization is not worth it, // use the original implementation. box match { case Zip(lhs, rhs, zipper) => val leftConstructor = makeConstructor(lhs) val rightConstructor = makeConstructor(rhs) results => { val leftValue = leftConstructor(results) val rightValue = rightConstructor(results) zipper.combine(leftValue, rightValue) } } } else { // If there 3 or more nested zips, we can optimize the zipper val zipper = makeZipperImpl(box, 0) val size = box.tupleSize results => { val array: Array[Any] = Array.ofDim(size) zipper(results)(array) arrayToTuple[A](array, size) } } } private def makeZipperImpl(box: Box[_], start: Int): Array[Any] => Array[Any] => Unit = { box match { case Zip(lhs, rhs, _) => val zipper1 = makeZipperImpl(lhs, start) val zipper2 = makeZipperImpl(rhs, start + lhs.tupleSize) results => builder => { zipper1(results)(builder) zipper2(results)(builder) } case map @ Map(_, _, tupleSize) => val constructor = makeConstructor(map) results => builder => { val result = constructor(results) tupleSize.spread(result, builder, start) } case succeed @ Succeed(_, tupleSize) => val constructor = makeConstructor(succeed) results => builder => { val result = constructor(results) tupleSize.spread(result, builder, start) } } } private def countNestedZips(box: Box[_]): Int = box match { case Zip(left, right, _) => 1 + countNestedZips(left) + countNestedZips(right) case Map(_, _, _) => 0 case Succeed(_, _) => 0 } private def arrayToTuple[A](array: Array[Any], size: Int): A = { println(s"Converting array ${array.toList} to tuple of size $size") size match { case 1 => array(0).asInstanceOf[A] case 2 => (array(0), array(1)).asInstanceOf[A] case 3 => (array(0), array(1), array(2)).asInstanceOf[A] case 4 => (array(0), array(1), array(2), array(3)).asInstanceOf[A] case 5 => (array(0), array(1), array(2), array(3), array(4)).asInstanceOf[A] case 6 => (array(0), array(1), array(2), array(3), array(4), array(5)).asInstanceOf[A] case 7 => (array(0), array(1), array(2), array(3), array(4), array(5), array(6)).asInstanceOf[A] case 8 => (array(0), array(1), array(2), array(3), array(4), array(5), array(6), array(7)).asInstanceOf[A] } } } object BoxExample extends App { val example = Box.succeed((10, ())) zip Box.succeed(2.0) zip Box.succeed(true) zip Box.succeed("b") // val example: Box[(Int, String, Double)] = // Box.succeed((1, "a")) zip Box.succeed(2.0) val constructor = Box.makeConstructor(example) val result = constructor(Array()) println(result) } sealed trait Zipper[A, B] extends Product with Serializable { type Out def combine(a: A, b: B): Out } object Zipper extends LowPriorityZipper1 { type WithOut[A, B, Out0] = Zipper[A, B] { type Out = Out0 } final case class Zipper2[A, B, C]() extends Zipper[(A, B), C] { type Out = (A, B, C) override def combine(a: (A, B), b: C): (A, B, C) = (a._1, a._2, b) } implicit def zipper2[A, B, C]: Zipper.WithOut[(A, B), C, (A, B, C)] = Zipper2() final case class Zipper3[A, B, C, D]() extends Zipper[(A, B, C), D] { type Out = (A, B, C, D) override def combine(a: (A, B, C), b: D): (A, B, C, D) = (a._1, a._2, a._3, b) } implicit def zipper3[A, B, C, D]: Zipper.WithOut[(A, B, C), D, (A, B, C, D)] = Zipper3() final case class Zipper4[A, B, C, D, E]() extends Zipper[(A, B, C, D), E] { type Out = (A, B, C, D, E) override def combine(a: (A, B, C, D), b: E): (A, B, C, D, E) = (a._1, a._2, a._3, a._4, b) } implicit def zipper4[A, B, C, D, E]: Zipper.WithOut[(A, B, C, D), E, (A, B, C, D, E)] = Zipper4() } trait LowPriorityZipper1 extends LowPriorityZipper0 { implicit def leftUnitZipper[A]: Zipper.WithOut[Unit, A, A] = LowPriorityZipper1.LeftUnitZipper[A]() implicit def rightUnitZipper[A]: Zipper.WithOut[A, Unit, A] = LowPriorityZipper1.RightUnitZipper[A]() } object LowPriorityZipper1 { final case class LeftUnitZipper[A]() extends Zipper[Unit, A] { type Out = A override def combine(a: Unit, b: A): A = b } final case class RightUnitZipper[A]() extends Zipper[A, Unit] { type Out = A override def combine(a: A, b: Unit): A = a } } trait TupleSize[A] { def size: Int def spread(value: A, array: Array[Any], start: Int): Unit } object TupleSize extends TupleSizeLowPri { def apply[A](implicit ts: TupleSize[A]): TupleSize[A] = ts implicit def unit: TupleSize[Unit] = new TupleSize[Unit] { override def size: Int = 0 override def spread(value: Unit, array: Array[Any], start: Int): Unit = () } implicit def tupleSize2[A, B]: TupleSize[(A, B)] = new TupleSize[(A, B)] { def size: Int = 2 override def spread(value: (A, B), array: Array[Any], start: Int): Unit = { array(start) = value._1 array(start + 1) = value._2 } } implicit def tupleSize3[A, B, C]: TupleSize[(A, B, C)] = new TupleSize[(A, B, C)] { def size: Int = 3 override def spread(value: (A, B, C), array: Array[Any], start: Int): Unit = { array(start) = value._1 array(start + 1) = value._2 array(start + 2) = value._3 } } implicit def tupleSize4[A, B, C, D]: TupleSize[(A, B, C, D)] = new TupleSize[(A, B, C, D)] { def size: Int = 4 override def spread(value: (A, B, C, D), array: Array[Any], start: Int): Unit = { array(start) = value._1 array(start + 1) = value._2 array(start + 2) = value._3 array(start + 3) = value._4 } } } trait TupleSizeLowPri { implicit def tupleSize[A]: TupleSize[A] = new TupleSize[A] { def size: Int = 1 override def spread(value: A, array: Array[Any], start: Int): Unit = { array(start) = value } } } trait LowPriorityZipper0 { implicit def abZipper[A, B]: Zipper.WithOut[A, B, (A, B)] = LowPriorityZipper0.ABZipper[A, B]() } object LowPriorityZipper0 { final case class ABZipper[A, B]() extends Zipper[A, B] { type Out = (A, B) override def combine(a: A, b: B): (A, B) = (a, b) } }