From 64cee08e2f7df7927ae66fff31598d21a93c25f2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 Feb 2022 10:49:29 +0100 Subject: [PATCH] Revert "Remove TupledFunction" This reverts commit f4e1ca4f565c74fbe1aa56ea88ce6f59409ccdf0. Also makes TupledFunction experimental --- .../dotty/tools/dotc/core/Definitions.scala | 4 + .../dotty/tools/dotc/typer/Synthesizer.scala | 45 +++++ .../reference/experimental/tupled-function.md | 82 +++++++++ docs/sidebar.yml | 1 + .../scala/runtime/TupledFunctions.scala | 165 ++++++++++++++++++ library/src/scala/util/TupledFunction.scala | 22 +++ project/MiMaFilters.scala | 8 +- .../erased/tupled-function-instances.scala | 0 .../neg/tupled-function-instances.scala | 2 +- tests/{disabled => }/pos/i7851.scala | 7 +- .../pos/tupled-function-instances.scala | 0 .../run/tupled-function-andThen.check | 0 .../run/tupled-function-andThen.scala | 0 .../run/tupled-function-apply.check | 0 .../run/tupled-function-apply.scala | 0 .../run/tupled-function-compose.check | 0 .../run/tupled-function-compose.scala | 0 .../tupled-function-extension-method.check | 0 .../tupled-function-extension-method.scala | 0 .../run/tupled-function-tupled.check | 0 .../run/tupled-function-tupled.scala | 0 .../run/tupled-function-untupled.scala | 0 22 files changed, 330 insertions(+), 6 deletions(-) create mode 100644 docs/_docs/reference/experimental/tupled-function.md create mode 100644 library/src-bootstrapped/scala/runtime/TupledFunctions.scala create mode 100644 library/src/scala/util/TupledFunction.scala rename tests/{disabled => }/neg-custom-args/erased/tupled-function-instances.scala (100%) rename tests/{disabled => }/neg/tupled-function-instances.scala (92%) rename tests/{disabled => }/pos/i7851.scala (86%) rename tests/{disabled => }/pos/tupled-function-instances.scala (100%) rename tests/{disabled => }/run/tupled-function-andThen.check (100%) rename tests/{disabled => }/run/tupled-function-andThen.scala (100%) rename tests/{disabled => }/run/tupled-function-apply.check (100%) rename tests/{disabled => }/run/tupled-function-apply.scala (100%) rename tests/{disabled => }/run/tupled-function-compose.check (100%) rename tests/{disabled => }/run/tupled-function-compose.scala (100%) rename tests/{disabled => }/run/tupled-function-extension-method.check (100%) rename tests/{disabled => }/run/tupled-function-extension-method.scala (100%) rename tests/{disabled => }/run/tupled-function-tupled.check (100%) rename tests/{disabled => }/run/tupled-function-tupled.scala (100%) rename tests/{disabled => }/run/tupled-function-untupled.scala (100%) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6fbced1bd377..9c56d695b9b8 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -884,6 +884,10 @@ class Definitions { lazy val RuntimeTuples_isInstanceOfEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfEmptyTuple") lazy val RuntimeTuples_isInstanceOfNonEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfNonEmptyTuple") + @tu lazy val TupledFunctionTypeRef: TypeRef = requiredClassRef("scala.util.TupledFunction") + def TupledFunctionClass(using Context): ClassSymbol = TupledFunctionTypeRef.symbol.asClass + def RuntimeTupleFunctionsModule(using Context): Symbol = requiredModule("scala.runtime.TupledFunctions") + // Annotation base classes @tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation") @tu lazy val ClassfileAnnotationClass: ClassSymbol = requiredClass("scala.annotation.ClassfileAnnotation") diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index b39fc4a01698..1ef86eab4a24 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -78,6 +78,50 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): } end synthesizedTypeTest + val synthesizedTupleFunction: SpecialHandler = (formal, span) => + formal match + case AppliedType(_, funArgs @ fun :: tupled :: Nil) => + def functionTypeEqual(baseFun: Type, actualArgs: List[Type], + actualRet: Type, expected: Type) = + expected =:= defn.FunctionOf(actualArgs, actualRet, + defn.isContextFunctionType(baseFun), defn.isErasedFunctionType(baseFun)) + val arity: Int = + if defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun) then -1 // TODO support? + else if defn.isFunctionType(fun) then + // 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) then + // TupledFunction[?, (...) => R] + tupled.dropDependentRefinement.dealias.argInfos match + case tupledArgs :: funRet :: Nil => + defn.tupleTypes(tupledArgs.dealias) 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 then + EmptyTree + else if arity <= Definitions.MaxImplementedFunctionArity then + ref(defn.RuntimeTupleFunctionsModule) + .select(s"tupledFunction$arity".toTermName) + .appliedToTypes(funArgs) + else + ref(defn.RuntimeTupleFunctionsModule) + .select("tupledFunctionXXL".toTermName) + .appliedToTypes(funArgs) + case _ => + EmptyTree + end synthesizedTupleFunction + /** If `formal` is of the form CanEqual[T, U], try to synthesize an * `CanEqual.canEqualAny[T, U]` as solution. */ @@ -483,6 +527,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): defn.TypeTestClass -> synthesizedTypeTest, defn.CanEqualClass -> synthesizedCanEqual, defn.ValueOfClass -> synthesizedValueOf, + defn.TupledFunctionClass -> synthesizedTupleFunction, defn.Mirror_ProductClass -> synthesizedProductMirror, defn.Mirror_SumClass -> synthesizedSumMirror, defn.MirrorClass -> synthesizedMirror, diff --git a/docs/_docs/reference/experimental/tupled-function.md b/docs/_docs/reference/experimental/tupled-function.md new file mode 100644 index 000000000000..da108fc832ad --- /dev/null +++ b/docs/_docs/reference/experimental/tupled-function.md @@ -0,0 +1,82 @@ +--- +layout: doc-page +title: "Tupled Function" +--- + +Tupled Function +---------------------- + +With functions bounded to arities up to 22 it was possible to generalize some operation on all function types using overloading. +Now that we have functions and tuples generalized to [arities above 22](../dropped-features/limit22.md) overloading is not an option anymore. +The type class `TupleFunction` provides a way to abstract directly over a function of any arity converting it to an equivalent function that receives all arguments in a single tuple. + +```scala +/** Type class relating a `FunctionN[..., R]` with an equivalent tupled function `Function1[TupleN[...], R]` + * + * @tparam F a function type + * @tparam G a tupled function type (function of arity 1 receiving a tuple as argument) + */ +@implicitNotFound("${F} cannot be tupled as ${G}") +sealed trait TupledFunction[F, G] { + def tupled(f: F): G + def untupled(g: G): F +} +``` + +The compiler will synthesize an instance of `TupledFunction[F, G]` if: + +* `F` is a function type of arity `N` +* `G` is a function with a single tuple argument of size `N` and its types are equal to the arguments of `F` +* The return type of `F` is equal to the return type of `G` +* `F` and `G` are the same sort of function (both are `(...) => R` or both are `(...) ?=> R`) +* If only one of `F` or `G` is instantiated the second one is inferred. + +Examples +-------- +`TupledFunction` can be used to generalize the `Function1.tupled`, ... `Function22.tupled` methods to functions of any arities. +The following defines `tupled` as [extension method](../contextual/extension-methods.html) ([full example](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-tupled.scala)). + +```scala +/** Creates a tupled version of this function: instead of N arguments, + * it accepts a single [[scala.Tuple]] with N elements as argument. + * + * @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 + */ +extension [F, Args <: Tuple, R](f: F) + def tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) +``` + +`TupledFunction` can be used to generalize the `Function.untupled` to a function of any arities ([full example](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-untupled.scala)) + +```scala +/** Creates an untupled version of this function: instead of a single argument of type [[scala.Tuple]] with N elements, + * it accepts 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 + */ +extension [F, Args <: Tuple, R](f: Args => R) + def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) +``` + +`TupledFunction` can also be used to generalize the [`Tuple1.compose`](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-compose.scala) and [`Tuple1.andThen`](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-andThen.scala) methods to compose functions of larger arities and with functions that return tuples. + +```scala +/** Composes two instances of TupledFunction into a new TupledFunction, with this function applied last. + * + * @tparam F a function type + * @tparam G a function type + * @tparam FArgs the tuple type with the same types as the function arguments of F and return type of G + * @tparam GArgs the tuple type with the same types as the function arguments of G + * @tparam R the return type of F + */ +extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) + def compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { + (x: GArgs) => tf.tupled(f)(tg.tupled(g)(x)) +} +``` diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 101de48b79bf..26b3878b6b6a 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -147,6 +147,7 @@ subsection: - page: reference/experimental/numeric-literals.md - page: reference/experimental/explicit-nulls.md - page: reference/experimental/cc.md + - page: reference/experimental/tupled-function.md - page: reference/syntax.md - title: Language Versions index: reference/language-versions/language-versions.md diff --git a/library/src-bootstrapped/scala/runtime/TupledFunctions.scala b/library/src-bootstrapped/scala/runtime/TupledFunctions.scala new file mode 100644 index 000000000000..c4010de1c754 --- /dev/null +++ b/library/src-bootstrapped/scala/runtime/TupledFunctions.scala @@ -0,0 +1,165 @@ +package scala.runtime + +import scala.util.TupledFunction +import scala.annotation.experimental + +@experimental +object TupledFunctions { + + def tupledFunction0[F, G]: TupledFunction[F, G] = TupledFunction[F, G]( + tupledImpl = (f: F) => ((args: EmptyTuple) => f.asInstanceOf[() => Any].apply()).asInstanceOf[G], + untupledImpl = (g: G) => (() => g.asInstanceOf[EmptyTuple => Any].apply(EmptyTuple)).asInstanceOf[F] + ) + + def tupledFunction1[F, G]: TupledFunction[F, G] = TupledFunction[F, G]( + tupledImpl = (f: F) => ((args: Tuple1[Any]) => f.asInstanceOf[Any => Any].apply(args._1)).asInstanceOf[G], + untupledImpl = (g: G) => ((x1: Any) => g.asInstanceOf[Tuple1[_] => Any].apply(Tuple1(x1))).asInstanceOf[F] + ) + + def tupledFunction2[F, G]: TupledFunction[F, G] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function2[_, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple2[_, _] => Any]).asInstanceOf[F] + ) + + def tupledFunction3[F, G]: TupledFunction[F, G] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function3[_, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple3[_, _, _] => Any]).asInstanceOf[F] + ) + + def tupledFunction4[F, G]: TupledFunction[F, G] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function4[_, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple4[_, _, _, _] => Any]).asInstanceOf[F] + ) + + def tupledFunction5[F, G]: TupledFunction[F, G] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function5[_, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple5[_, _, _, _, _] => Any]).asInstanceOf[F] + ) + + def tupledFunction6[F, G]: TupledFunction[F, G] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function6[_, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function7[_, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function8[_, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function9[_, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function10[_, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function11[_, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function12[_, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function13[_, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function14[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => f.asInstanceOf[Function22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G], + untupledImpl = (g: G) => + ((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] = TupledFunction[F, G]( + tupledImpl = (f: F) => ((args: TupleXXL) => f.asInstanceOf[FunctionXXL].apply(args.elems)).asInstanceOf[G], + untupledImpl = (g: G) => new FunctionXXL { + override def apply(xs: IArray[Object]): AnyRef = g.asInstanceOf[TupleXXL => AnyRef].apply(TupleXXL.fromIArray(xs)) + }.asInstanceOf[F] + ) + +} diff --git a/library/src/scala/util/TupledFunction.scala b/library/src/scala/util/TupledFunction.scala new file mode 100644 index 000000000000..99df6b5b7cae --- /dev/null +++ b/library/src/scala/util/TupledFunction.scala @@ -0,0 +1,22 @@ +package scala.util + +import scala.annotation.implicitNotFound +import scala.annotation.experimental + +/** Type class relating a `FunctionN[..., R]` with an equivalent tupled function `Function1[TupleN[...], R]` + * + * @tparam F a function type + * @tparam G a tupled function type (function of arity 1 receiving a tuple as argument) + */ +@implicitNotFound("${F} cannot be tupled as ${G}") +@experimental +sealed trait TupledFunction[F, G]: + def tupled(f: F): G + def untupled(g: G): F + +@experimental +private[scala] object TupledFunction: + def apply[F, G](tupledImpl: F => G, untupledImpl: G => F): TupledFunction[F, G] = + new TupledFunction[F, G]: + def tupled(f: F): G = tupledImpl(f) + def untupled(g: G): F = untupledImpl(g) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index cac81eecc141..02432758a391 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -3,7 +3,7 @@ import com.typesafe.tools.mima.core._ object MiMaFilters { val Library: Seq[ProblemFilter] = Seq( - // Experimental APIs that can be added in 3.2.0 + // Experimental APIs that can be added in 3.2.0 or later ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.Tuples.append"), ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeReprMethods.substituteTypes"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeReprMethods.substituteTypes"), @@ -18,6 +18,12 @@ object MiMaFilters { ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#CompilationInfoModule.XmacroSettings"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#CompilationInfoModule.XmacroSettings"), + // TupledFunction + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.TupledFunctions"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.TupledFunctions$"), + ProblemFilters.exclude[MissingClassProblem]("scala.util.TupledFunction"), + ProblemFilters.exclude[MissingClassProblem]("scala.util.TupledFunction$"), + // Private to the compiler - needed for forward binary compatibility ProblemFilters.exclude[MissingClassProblem]("scala.annotation.since") ) diff --git a/tests/disabled/neg-custom-args/erased/tupled-function-instances.scala b/tests/neg-custom-args/erased/tupled-function-instances.scala similarity index 100% rename from tests/disabled/neg-custom-args/erased/tupled-function-instances.scala rename to tests/neg-custom-args/erased/tupled-function-instances.scala diff --git a/tests/disabled/neg/tupled-function-instances.scala b/tests/neg/tupled-function-instances.scala similarity index 92% rename from tests/disabled/neg/tupled-function-instances.scala rename to tests/neg/tupled-function-instances.scala index 76ddc347a1b2..b07ab914ea8b 100644 --- a/tests/disabled/neg/tupled-function-instances.scala +++ b/tests/neg/tupled-function-instances.scala @@ -9,7 +9,7 @@ object Test { summon[TupledFunction[Any, ((T, T, T)) => R]] // error summon[TupledFunction[Tuple1[Int], ((T, T, T)) => R]] // error - summon[TupledFunction[(T, T, T))=> R, Nothing]] // error + summon[TupledFunction[(T, T, T) => R, Nothing]] // error summon[TupledFunction[(T, T, T) => R, Any]] // error summon[TupledFunction[((T, T, T)) => R, Tuple1[Int]]] // error diff --git a/tests/disabled/pos/i7851.scala b/tests/pos/i7851.scala similarity index 86% rename from tests/disabled/pos/i7851.scala rename to tests/pos/i7851.scala index 2098a04f068a..7be04c45a257 100644 --- a/tests/disabled/pos/i7851.scala +++ b/tests/pos/i7851.scala @@ -8,12 +8,11 @@ object Wrapper { type Aux[T <: Tuple, WrappedT0 <: Tuple] = Wrapper[T] { type Wr given Wrapper[EmptyTuple] with { type WrappedT = EmptyTuple } -given [T: Wrappable]: Wrapper[T] with with { type WrappedT = Wrapped[T] } +given [T: Wrappable]: Wrapper[T] with { type WrappedT = Wrapped[T] } -given [H: Wrappable, T <: Tuple, WrappedT0 <: Tuple] - (using Wrapper.Aux[T, WrappedT0]) - as Wrapper[H *: T]: +given [H: Wrappable, T <: Tuple, WrappedT0 <: Tuple](using Wrapper.Aux[T, WrappedT0]): Wrapper[H *: T] with { type WrappedT = Wrapped[H] *: WrappedT0 +} def wrappedFunction[F, FArgs <: Tuple, WrapperFArgs <: Tuple, R: Wrappable]( function: F diff --git a/tests/disabled/pos/tupled-function-instances.scala b/tests/pos/tupled-function-instances.scala similarity index 100% rename from tests/disabled/pos/tupled-function-instances.scala rename to tests/pos/tupled-function-instances.scala diff --git a/tests/disabled/run/tupled-function-andThen.check b/tests/run/tupled-function-andThen.check similarity index 100% rename from tests/disabled/run/tupled-function-andThen.check rename to tests/run/tupled-function-andThen.check diff --git a/tests/disabled/run/tupled-function-andThen.scala b/tests/run/tupled-function-andThen.scala similarity index 100% rename from tests/disabled/run/tupled-function-andThen.scala rename to tests/run/tupled-function-andThen.scala diff --git a/tests/disabled/run/tupled-function-apply.check b/tests/run/tupled-function-apply.check similarity index 100% rename from tests/disabled/run/tupled-function-apply.check rename to tests/run/tupled-function-apply.check diff --git a/tests/disabled/run/tupled-function-apply.scala b/tests/run/tupled-function-apply.scala similarity index 100% rename from tests/disabled/run/tupled-function-apply.scala rename to tests/run/tupled-function-apply.scala diff --git a/tests/disabled/run/tupled-function-compose.check b/tests/run/tupled-function-compose.check similarity index 100% rename from tests/disabled/run/tupled-function-compose.check rename to tests/run/tupled-function-compose.check diff --git a/tests/disabled/run/tupled-function-compose.scala b/tests/run/tupled-function-compose.scala similarity index 100% rename from tests/disabled/run/tupled-function-compose.scala rename to tests/run/tupled-function-compose.scala diff --git a/tests/disabled/run/tupled-function-extension-method.check b/tests/run/tupled-function-extension-method.check similarity index 100% rename from tests/disabled/run/tupled-function-extension-method.check rename to tests/run/tupled-function-extension-method.check diff --git a/tests/disabled/run/tupled-function-extension-method.scala b/tests/run/tupled-function-extension-method.scala similarity index 100% rename from tests/disabled/run/tupled-function-extension-method.scala rename to tests/run/tupled-function-extension-method.scala diff --git a/tests/disabled/run/tupled-function-tupled.check b/tests/run/tupled-function-tupled.check similarity index 100% rename from tests/disabled/run/tupled-function-tupled.check rename to tests/run/tupled-function-tupled.check diff --git a/tests/disabled/run/tupled-function-tupled.scala b/tests/run/tupled-function-tupled.scala similarity index 100% rename from tests/disabled/run/tupled-function-tupled.scala rename to tests/run/tupled-function-tupled.scala diff --git a/tests/disabled/run/tupled-function-untupled.scala b/tests/run/tupled-function-untupled.scala similarity index 100% rename from tests/disabled/run/tupled-function-untupled.scala rename to tests/run/tupled-function-untupled.scala