diff --git a/akka-http-tests/src/test/scala/akka/http/server/util/TupleOpsSpec.scala b/akka-http-tests/src/test/scala/akka/http/server/util/TupleOpsSpec.scala new file mode 100644 index 0000000000..34bd3d491f --- /dev/null +++ b/akka-http-tests/src/test/scala/akka/http/server/util/TupleOpsSpec.scala @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package akka.http.server.util + +import org.scalatest.{ Matchers, FreeSpec } + +class TupleOpsSpec extends FreeSpec with Matchers { + import TupleOps._ + + "The TupleOps should" - { + + "support folding over tuples using a binary poly-function" - { + + "example 1" in { + object Funky extends Poly2 { + implicit def step1 = at[Double, Int](_ + _) + implicit def step2 = at[Double, Symbol]((d, s) ⇒ (d + s.name.tail.toInt).toByte) + implicit def step3 = at[Byte, String]((byte, s) ⇒ byte + s.toLong) + } + (1, 'X2, "3").foldLeft(0.0)(Funky) shouldEqual 6L + } + } + } +} \ No newline at end of file diff --git a/akka-http/src/main/boilerplate/akka/http/server/util/TupleFoldInstances.scala.template b/akka-http/src/main/boilerplate/akka/http/server/util/TupleFoldInstances.scala.template new file mode 100644 index 0000000000..a73c72d900 --- /dev/null +++ b/akka-http/src/main/boilerplate/akka/http/server/util/TupleFoldInstances.scala.template @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package akka.http.server.util + +import TupleOps.FoldLeft +import TupleOps.FoldLeft.Aux +import Poly2.Case + +private[util] abstract class TupleFoldInstances { + + [2..22#implicit def t1[In, [2..#T0#], X, T1, Op](implicit fold: Aux[In, Tuple0[[2..#T0#]], Op, X], f: Case[X, T1, Op]): Aux[In, Tuple1[[#T1#]], Op, f.Out] = + new FoldLeft[In, Tuple1[[#T1#]], Op] { + type Out = f.Out + def apply(zero: In, tuple: Tuple1[[#T1#]]) = { + val ([#v1#]) = tuple + f(fold(zero, Tuple0([2..#v0#])), v1) + } + }# + ] +} \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/server/util/TupleOps.scala b/akka-http/src/main/scala/akka/http/server/util/TupleOps.scala new file mode 100644 index 0000000000..8ecf22fe86 --- /dev/null +++ b/akka-http/src/main/scala/akka/http/server/util/TupleOps.scala @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +package akka.http.server.util + +class TupleOps[T](val tuple: T) extends AnyVal { + import TupleOps._ + + def foldLeft[In](zero: In)(op: Poly2)(implicit fold: FoldLeft[In, T, op.type]): fold.Out = fold(zero, tuple) +} + +object TupleOps { + implicit def enhanceTuple[T: Tuple](tuple: T) = new TupleOps(tuple) + + trait FoldLeft[In, T, Op] { + type Out + def apply(zero: In, tuple: T): Out + } + object FoldLeft extends TupleFoldInstances { + import Poly2.Case + + type Aux[In, T, Op, Out0] = FoldLeft[In, T, Op] { type Out = Out0 } + + implicit def t0[In, Op]: Aux[In, Unit, Op, In] = + new FoldLeft[In, Unit, Op] { + type Out = In + def apply(zero: In, tuple: Unit) = zero + } + + implicit def t1[In, A, Op](implicit f: Case[In, A, Op]): Aux[In, Tuple1[A], Op, f.Out] = + new FoldLeft[In, Tuple1[A], Op] { + type Out = f.Out + def apply(zero: In, tuple: Tuple1[A]) = f(zero, tuple._1) + } + } +} + +trait Poly2 { + def at[A, B] = new CaseBuilder[A, B] + class CaseBuilder[A, B] { + def apply[R](f: (A, B) ⇒ R) = new Poly2.Case[A, B, Poly2.this.type] { + type Out = R + def apply(a: A, b: B) = f(a, b) + } + } +} +object Poly2 { + trait Case[A, B, Op] { + type Out + def apply(a: A, b: B): Out + } +} +