diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 51d64b248bb9..f24bc65b7ae1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -10,6 +10,8 @@ import collection.mutable import Denotations.SingleDenotation import util.SimpleIdentityMap +import scala.annotation.tailrec + object Definitions { /** The maximum number of elements in a tuple or product. @@ -1198,6 +1200,17 @@ class Definitions { else TypeOps.nestedPairs(elems) } + def tupleTypes(tp: Type, bound: Int = Int.MaxValue)(implicit ctx: Context): Option[List[Type]] = { + @tailrec def rec(tp: Type, acc: List[Type], bound: Int): Option[List[Type]] = tp match { + case _ if bound < 0 => Some(acc.reverse) + case tp: AppliedType if defn.PairClass == tp.classSymbol => rec(tp.args(1), tp.args.head :: acc, bound - 1) + case tp: AppliedType if defn.isTupleClass(tp.tycon.classSymbol) => Some(acc.reverse ::: tp.args) + case tp if tp.classSymbol == defn.UnitClass => Some(acc.reverse) + case _ => None + } + rec(tp.stripTypeVar, Nil, bound) + } + def isProductSubType(tp: Type)(implicit ctx: Context): Boolean = tp.derivesFrom(ProductType.symbol) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 430658acae72..af7d0c31d621 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -711,23 +711,44 @@ trait Implicits { self: Typer => def synthesizedTupleFunction(formal: Type): Tree = { formal match { - case AppliedType(_, funArgs @ fun :: tupled :: Nil) if defn.isFunctionType(fun) && defn.isFunctionType(tupled) => - lazy val funTypes = fun.dropDependentRefinement.dealias.argInfos - lazy val tupledTypes = tupled.dropDependentRefinement.dealias.argInfos - if ( - defn.isImplicitFunctionType(fun) == defn.isImplicitFunctionType(tupled) && - tupledTypes.size == 2 && - defn.tupleType(funTypes.init) =:= tupledTypes.head && - funTypes.last =:= tupledTypes.last - ) { - val arity = funTypes.size - 1 - if (defn.isErasedFunctionType(fun)) - EmptyTree // TODO support? - else if (arity <= Definitions.MaxImplementedFunctionArity) - ref(defn.InternalTupleFunctionModule).select(s"tupledFunction$arity".toTermName).appliedToTypes(funArgs) - else - ref(defn.InternalTupleFunctionModule).select("tupledFunctionXXL".toTermName).appliedToTypes(funArgs) - } else EmptyTree + case AppliedType(_, funArgs @ fun :: tupled :: Nil) => + def functionTypeEqual(baseFun: Type, actualArgs: List[Type], actualRet: Type, expected: Type) = { + expected =:= defn.FunctionOf(actualArgs, actualRet, defn.isImplicitFunctionType(baseFun), defn.isErasedFunctionType(baseFun)) + } + val arity: Int = { + if (defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun)) -1 // TODO support? + else if (defn.isFunctionType(fun)) { + // TupledFunction[(...) => R, ?] + fun.dropDependentRefinement.dealias.argInfos match { + case funArgs :+ funRet if functionTypeEqual(fun, defn.tupleType(funArgs) :: Nil, funRet, tupled) => + // TupledFunction[(...funArgs...) => funRet, ?] + funArgs.size + case _ => -1 + } + } else if (defn.isFunctionType(tupled)) { + // TupledFunction[?, (...) => R] + tupled.dropDependentRefinement.dealias.argInfos match { + case tupledArgs :: funRet :: Nil => + defn.tupleTypes(tupledArgs) match { + case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) => + // TupledFunction[?, ((...funArgs...)) => funRet] + funArgs.size + case _ => -1 + } + case _ => -1 + } + } + else { + // TupledFunction[?, ?] + -1 + } + } + if (arity == -1) + EmptyTree + else if (arity <= Definitions.MaxImplementedFunctionArity) + ref(defn.InternalTupleFunctionModule).select(s"tupledFunction$arity".toTermName).appliedToTypes(funArgs) + else + ref(defn.InternalTupleFunctionModule).select("tupledFunctionXXL".toTermName).appliedToTypes(funArgs) case _ => EmptyTree } diff --git a/library/src-3.x/scala/TupledFunction.scala b/library/src-3.x/scala/TupledFunction.scala index 1d500d1ed23d..c15e63c1b4fc 100644 --- a/library/src-3.x/scala/TupledFunction.scala +++ b/library/src-3.x/scala/TupledFunction.scala @@ -10,6 +10,7 @@ import scala.annotation.implicitNotFound @implicitNotFound("${F} cannot be tupled as ${G}") sealed trait TupledFunction[F, G] { def tuple(f: F): G + def untuple(g: G): F } package internal { @@ -17,102 +18,159 @@ package internal { object TupledFunction { def tupledFunction0[F, G]: TupledFunction[F, G] = new TupledFunction { - def tuple(f: F): G = - ((args: Unit) => f.asInstanceOf[() => Any].apply()).asInstanceOf[G] + def tuple(f: F): G = ((args: Unit) => f.asInstanceOf[() => Any].apply()).asInstanceOf[G] + def untuple(g: G): F = (() => g.asInstanceOf[Unit => Any].apply(())).asInstanceOf[F] } def tupledFunction1[F, G]: TupledFunction[F, G] = new TupledFunction { - def tuple(f: F): G = - ((args: Tuple1[Any]) => f.asInstanceOf[Any => Any].apply(args._1)).asInstanceOf[G] + def tuple(f: F): G = ((args: Tuple1[Any]) => f.asInstanceOf[Any => Any].apply(args._1)).asInstanceOf[G] + def untuple(g: G): F = ((x1: Any) => g.asInstanceOf[Tuple1[_] => Any].apply(Tuple1(x1))).asInstanceOf[F] } def tupledFunction2[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function2[_, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = Function.untupled(g.asInstanceOf[Tuple2[_, _] => Any]).asInstanceOf[F] } def tupledFunction3[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function3[_, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = Function.untupled(g.asInstanceOf[Tuple3[_, _, _] => Any]).asInstanceOf[F] } def tupledFunction4[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function4[_, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = Function.untupled(g.asInstanceOf[Tuple4[_, _, _, _] => Any]).asInstanceOf[F] } def tupledFunction5[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function5[_, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = Function.untupled(g.asInstanceOf[Tuple5[_, _, _, _, _] => Any]).asInstanceOf[F] } def tupledFunction6[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function6[_, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any) => + g.asInstanceOf[Tuple6[_, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6))).asInstanceOf[F] } def tupledFunction7[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function7[_, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any) => + g.asInstanceOf[Tuple7[_, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7))).asInstanceOf[F] } def tupledFunction8[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function8[_, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any) => + g.asInstanceOf[Tuple8[_, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8))).asInstanceOf[F] } def tupledFunction9[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function9[_, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any) => + g.asInstanceOf[Tuple9[_, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9))).asInstanceOf[F] } def tupledFunction10[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function10[_, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any) => + g.asInstanceOf[Tuple10[_, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10))).asInstanceOf[F] } def tupledFunction11[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function11[_, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any) => + g.asInstanceOf[Tuple11[_, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11))).asInstanceOf[F] } def tupledFunction12[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function12[_, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any) => + g.asInstanceOf[Tuple12[_, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12))).asInstanceOf[F] } def tupledFunction13[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function13[_, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any) => + g.asInstanceOf[Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13))).asInstanceOf[F] } def tupledFunction14[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function14[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any) => + g.asInstanceOf[Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14))).asInstanceOf[F] } def tupledFunction15[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any) => + g.asInstanceOf[Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15))).asInstanceOf[F] } def tupledFunction16[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any, x16: Any) => + g.asInstanceOf[Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16))).asInstanceOf[F] } def tupledFunction17[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any, x16: Any, x17: Any) => + g.asInstanceOf[Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17))).asInstanceOf[F] } def tupledFunction18[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any, x16: Any, x17: Any, x18: Any) => + g.asInstanceOf[Tuple18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18))).asInstanceOf[F] } def tupledFunction19[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any, x16: Any, x17: Any, x18: Any, x19: Any) => + g.asInstanceOf[Tuple19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19))).asInstanceOf[F] } def tupledFunction20[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any, x16: Any, x17: Any, x18: Any, x19: Any, x20: Any) => + g.asInstanceOf[Tuple20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20))).asInstanceOf[F] } def tupledFunction21[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any, x16: Any, x17: Any, x18: Any, x19: Any, x20: Any, x21: Any) => + g.asInstanceOf[Tuple21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21))).asInstanceOf[F] } def tupledFunction22[F, G]: TupledFunction[F, G] = new TupledFunction { def tuple(f: F): G = f.asInstanceOf[Function22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G] + def untuple(g: G): F = + ((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any, x13: Any, x14: Any, x15: Any, x16: Any, x17: Any, x18: Any, x19: Any, x20: Any, x21: Any, x22: Any) => + g.asInstanceOf[Tuple22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22))).asInstanceOf[F] } def tupledFunctionXXL[F, G]: TupledFunction[F, G] = new TupledFunction { - def tuple(f: F): G = - ((args: TupleXXL) => f.asInstanceOf[FunctionXXL].apply(args.elems)).asInstanceOf[G] + def tuple(f: F): G = ((args: TupleXXL) => f.asInstanceOf[FunctionXXL].apply(args.elems)).asInstanceOf[G] + def untuple(g: G): F = new FunctionXXL { + override def apply(xs: Array[Object]): AnyRef = g.asInstanceOf[TupleXXL => AnyRef].apply(TupleXXL(xs)) + }.asInstanceOf[F] } } diff --git a/tests/run/tupled-function-untupled.scala b/tests/run/tupled-function-untupled.scala new file mode 100644 index 000000000000..9575e992df4c --- /dev/null +++ b/tests/run/tupled-function-untupled.scala @@ -0,0 +1,108 @@ +object Test { + def main(args: Array[String]): Unit = { + + val f0 = (args: Unit) => 0 + val g0: () => Int = f0.untupled + println(g0()) + + val f1 = (args: Tuple1[Int]) => 1 + val g1: (Int) => Int = f1.untupled + println(g1(1)) + + val f2 = (args: (Int, Int)) => 2 + val g2: (Int, Int) => Int = f2.untupled + println(g2(1, 2)) + + val f3 = (args: (Int, Int, Int)) => 3 + val g3: (Int, Int, Int) => Int = f3.untupled + println(g3(1, 2, 3)) + + val f4 = (args: (Int, Int, Int, Int)) => 4 + val g4: (Int, Int, Int, Int) => Int = f4.untupled + println(g4(1, 2, 3, 4)) + + val f5 = (args: (Int, Int, Int, Int, Int)) => 5 + val g5: (Int, Int, Int, Int, Int) => Int = f5.untupled + println(g5(1, 2, 3, 4, 5)) + + val f6 = (args: (Int, Int, Int, Int, Int, Int)) => 6 + val g6: (Int, Int, Int, Int, Int, Int) => Int = f6.untupled + println(g6(1, 2, 3, 4, 5, 6)) + + val f7 = (args: (Int, Int, Int, Int, Int, Int, Int)) => 7 + val g7: (Int, Int, Int, Int, Int, Int, Int) => Int = f7.untupled + println(g7(1, 2, 3, 4, 5, 6, 7)) + + val f8 = (args: (Int, Int, Int, Int, Int, Int, Int, Int)) => 8 + val g8: (Int, Int, Int, Int, Int, Int, Int, Int) => Int = f8.untupled + println(g8(1, 2, 3, 4, 5, 6, 7, 8)) + + val f9 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 9 + val g9: (Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f9.untupled + println(g9(1, 2, 3, 4, 5, 6, 7, 8, 9)) + + val f10 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 10 + val g10: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f10.untupled + println(g10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) + + val f11 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 11 + val g11: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f11.untupled + println(g11(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)) + + val f12 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 12 + val g12: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f12.untupled + println(g12(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)) + + val f13 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 13 + val g13: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f13.untupled + println(g13(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)) + + val f14 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 14 + val g14: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f14.untupled + println(g14(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)) + + val f15 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 15 + val g15: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f15.untupled + println(g15(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)) + + val f16 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 16 + val g16: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f16.untupled + println(g16(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)) + + val f17 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 17 + val g17: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f17.untupled + println(g17(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17)) + + val f18 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 18 + val g18: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f18.untupled + println(g18(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)) + + val f19 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 19 + val g19: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f19.untupled + println(g19(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19)) + + val f20 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 20 + val g20: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f20.untupled + println(g20(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)) + + val f21 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 21 + val g21: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f21.untupled + println(g21(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)) + + val f22 = (args: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => 22 + val g22: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) => Int = f22.untupled + println(g22(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)) + + } + + /** Creates an untupled version of this function: instead of single [[scala.Tuple]] argument, + * it accepts a N arguments. + * + * This is a generalization of [[scala.Function.untupled]] that work on functions of any arity + * + * @tparam F the function type + * @tparam Args the tuple type with the same types as the function arguments of F + * @tparam R the return type of F + */ + def (f: Args => R) untupled[F, Args <: Tuple, R] given (tf: TupledFunction[F, Args => R]): F = tf.untuple(f) +}