Skip to content

Commit cb824bf

Browse files
Merge pull request #14414 from dotty-staging/add-experimental-tupled-functions
Add experimental TupledFunction
2 parents aefb2b3 + 4b0906d commit cb824bf

22 files changed

+354
-30
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,10 @@ class Definitions {
884884
lazy val RuntimeTuples_isInstanceOfEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfEmptyTuple")
885885
lazy val RuntimeTuples_isInstanceOfNonEmptyTuple: Symbol = RuntimeTuplesModule.requiredMethod("isInstanceOfNonEmptyTuple")
886886

887+
@tu lazy val TupledFunctionTypeRef: TypeRef = requiredClassRef("scala.util.TupledFunction")
888+
def TupledFunctionClass(using Context): ClassSymbol = TupledFunctionTypeRef.symbol.asClass
889+
def RuntimeTupleFunctionsModule(using Context): Symbol = requiredModule("scala.runtime.TupledFunctions")
890+
887891
// Annotation base classes
888892
@tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation")
889893
@tu lazy val ClassfileAnnotationClass: ClassSymbol = requiredClass("scala.annotation.ClassfileAnnotation")

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,50 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
7878
}
7979
end synthesizedTypeTest
8080

81+
val synthesizedTupleFunction: SpecialHandler = (formal, span) =>
82+
formal match
83+
case AppliedType(_, funArgs @ fun :: tupled :: Nil) =>
84+
def functionTypeEqual(baseFun: Type, actualArgs: List[Type],
85+
actualRet: Type, expected: Type) =
86+
expected =:= defn.FunctionOf(actualArgs, actualRet,
87+
defn.isContextFunctionType(baseFun), defn.isErasedFunctionType(baseFun))
88+
val arity: Int =
89+
if defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun) then -1 // TODO support?
90+
else if defn.isFunctionType(fun) then
91+
// TupledFunction[(...) => R, ?]
92+
fun.dropDependentRefinement.dealias.argInfos match
93+
case funArgs :+ funRet
94+
if functionTypeEqual(fun, defn.tupleType(funArgs) :: Nil, funRet, tupled) =>
95+
// TupledFunction[(...funArgs...) => funRet, ?]
96+
funArgs.size
97+
case _ => -1
98+
else if defn.isFunctionType(tupled) then
99+
// TupledFunction[?, (...) => R]
100+
tupled.dropDependentRefinement.dealias.argInfos match
101+
case tupledArgs :: funRet :: Nil =>
102+
defn.tupleTypes(tupledArgs.dealias) match
103+
case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) =>
104+
// TupledFunction[?, ((...funArgs...)) => funRet]
105+
funArgs.size
106+
case _ => -1
107+
case _ => -1
108+
else
109+
// TupledFunction[?, ?]
110+
-1
111+
if arity == -1 then
112+
EmptyTree
113+
else if arity <= Definitions.MaxImplementedFunctionArity then
114+
ref(defn.RuntimeTupleFunctionsModule)
115+
.select(s"tupledFunction$arity".toTermName)
116+
.appliedToTypes(funArgs)
117+
else
118+
ref(defn.RuntimeTupleFunctionsModule)
119+
.select("tupledFunctionXXL".toTermName)
120+
.appliedToTypes(funArgs)
121+
case _ =>
122+
EmptyTree
123+
end synthesizedTupleFunction
124+
81125
/** If `formal` is of the form CanEqual[T, U], try to synthesize an
82126
* `CanEqual.canEqualAny[T, U]` as solution.
83127
*/
@@ -489,6 +533,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
489533
defn.TypeTestClass -> synthesizedTypeTest,
490534
defn.CanEqualClass -> synthesizedCanEqual,
491535
defn.ValueOfClass -> synthesizedValueOf,
536+
defn.TupledFunctionClass -> synthesizedTupleFunction,
492537
defn.Mirror_ProductClass -> synthesizedProductMirror,
493538
defn.Mirror_SumClass -> synthesizedSumMirror,
494539
defn.MirrorClass -> synthesizedMirror,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
layout: doc-page
3+
title: "Tupled Function"
4+
---
5+
6+
Tupled Function
7+
----------------------
8+
9+
With functions bounded to arities up to 22 it was possible to generalize some operation on all function types using overloading.
10+
Now that we have functions and tuples generalized to [arities above 22](../dropped-features/limit22.md) overloading is not an option anymore.
11+
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.
12+
13+
```scala
14+
/** Type class relating a `FunctionN[..., R]` with an equivalent tupled function `Function1[TupleN[...], R]`
15+
*
16+
* @tparam F a function type
17+
* @tparam G a tupled function type (function of arity 1 receiving a tuple as argument)
18+
*/
19+
@implicitNotFound("${F} cannot be tupled as ${G}")
20+
sealed trait TupledFunction[F, G] {
21+
def tupled(f: F): G
22+
def untupled(g: G): F
23+
}
24+
```
25+
26+
The compiler will synthesize an instance of `TupledFunction[F, G]` if:
27+
28+
* `F` is a function type of arity `N`
29+
* `G` is a function with a single tuple argument of size `N` and its types are equal to the arguments of `F`
30+
* The return type of `F` is equal to the return type of `G`
31+
* `F` and `G` are the same sort of function (both are `(...) => R` or both are `(...) ?=> R`)
32+
* If only one of `F` or `G` is instantiated the second one is inferred.
33+
34+
Examples
35+
--------
36+
`TupledFunction` can be used to generalize the `Function1.tupled`, ... `Function22.tupled` methods to functions of any arities.
37+
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)).
38+
39+
```scala
40+
/** Creates a tupled version of this function: instead of N arguments,
41+
* it accepts a single [[scala.Tuple]] with N elements as argument.
42+
*
43+
* @tparam F the function type
44+
* @tparam Args the tuple type with the same types as the function arguments of F
45+
* @tparam R the return type of F
46+
*/
47+
extension [F, Args <: Tuple, R](f: F)
48+
def tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f)
49+
```
50+
51+
`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))
52+
53+
```scala
54+
/** Creates an untupled version of this function: instead of a single argument of type [[scala.Tuple]] with N elements,
55+
* it accepts N arguments.
56+
*
57+
* This is a generalization of [[scala.Function.untupled]] that work on functions of any arity
58+
*
59+
* @tparam F the function type
60+
* @tparam Args the tuple type with the same types as the function arguments of F
61+
* @tparam R the return type of F
62+
*/
63+
extension [F, Args <: Tuple, R](f: Args => R)
64+
def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f)
65+
```
66+
67+
`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.
68+
69+
```scala
70+
/** Composes two instances of TupledFunction into a new TupledFunction, with this function applied last.
71+
*
72+
* @tparam F a function type
73+
* @tparam G a function type
74+
* @tparam FArgs the tuple type with the same types as the function arguments of F and return type of G
75+
* @tparam GArgs the tuple type with the same types as the function arguments of G
76+
* @tparam R the return type of F
77+
*/
78+
extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F)
79+
def compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = {
80+
(x: GArgs) => tf.tupled(f)(tg.tupled(g)(x))
81+
}
82+
```

docs/sidebar.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ subsection:
148148
- page: reference/experimental/numeric-literals.md
149149
- page: reference/experimental/explicit-nulls.md
150150
- page: reference/experimental/cc.md
151+
- page: reference/experimental/tupled-function.md
151152
- page: reference/syntax.md
152153
- title: Language Versions
153154
index: reference/language-versions/language-versions.md
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package scala.runtime
2+
3+
import scala.util.TupledFunction
4+
import scala.annotation.experimental
5+
6+
@experimental
7+
object TupledFunctions {
8+
9+
def tupledFunction0[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
10+
tupledImpl = (f: F) => ((args: EmptyTuple) => f.asInstanceOf[() => Any].apply()).asInstanceOf[G],
11+
untupledImpl = (g: G) => (() => g.asInstanceOf[EmptyTuple => Any].apply(EmptyTuple)).asInstanceOf[F]
12+
)
13+
14+
def tupledFunction1[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
15+
tupledImpl = (f: F) => ((args: Tuple1[Any]) => f.asInstanceOf[Any => Any].apply(args._1)).asInstanceOf[G],
16+
untupledImpl = (g: G) => ((x1: Any) => g.asInstanceOf[Tuple1[_] => Any].apply(Tuple1(x1))).asInstanceOf[F]
17+
)
18+
19+
def tupledFunction2[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
20+
tupledImpl = (f: F) => f.asInstanceOf[Function2[_, _, _]].tupled.asInstanceOf[G],
21+
untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple2[_, _] => Any]).asInstanceOf[F]
22+
)
23+
24+
def tupledFunction3[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
25+
tupledImpl = (f: F) => f.asInstanceOf[Function3[_, _, _, _]].tupled.asInstanceOf[G],
26+
untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple3[_, _, _] => Any]).asInstanceOf[F]
27+
)
28+
29+
def tupledFunction4[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
30+
tupledImpl = (f: F) => f.asInstanceOf[Function4[_, _, _, _, _]].tupled.asInstanceOf[G],
31+
untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple4[_, _, _, _] => Any]).asInstanceOf[F]
32+
)
33+
34+
def tupledFunction5[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
35+
tupledImpl = (f: F) => f.asInstanceOf[Function5[_, _, _, _, _, _]].tupled.asInstanceOf[G],
36+
untupledImpl = (g: G) => Function.untupled(g.asInstanceOf[Tuple5[_, _, _, _, _] => Any]).asInstanceOf[F]
37+
)
38+
39+
def tupledFunction6[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
40+
tupledImpl = (f: F) => f.asInstanceOf[Function6[_, _, _, _, _, _, _]].tupled.asInstanceOf[G],
41+
untupledImpl = (g: G) =>
42+
((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any) =>
43+
g.asInstanceOf[Tuple6[_, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6))).asInstanceOf[F]
44+
)
45+
46+
def tupledFunction7[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
47+
tupledImpl = (f: F) => f.asInstanceOf[Function7[_, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
48+
untupledImpl = (g: G) =>
49+
((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any) =>
50+
g.asInstanceOf[Tuple7[_, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7))).asInstanceOf[F]
51+
)
52+
53+
def tupledFunction8[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
54+
tupledImpl = (f: F) => f.asInstanceOf[Function8[_, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
55+
untupledImpl = (g: G) =>
56+
((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any) =>
57+
g.asInstanceOf[Tuple8[_, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8))).asInstanceOf[F]
58+
)
59+
60+
def tupledFunction9[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
61+
tupledImpl = (f: F) => f.asInstanceOf[Function9[_, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
62+
untupledImpl = (g: G) =>
63+
((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any) =>
64+
g.asInstanceOf[Tuple9[_, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9))).asInstanceOf[F]
65+
)
66+
67+
def tupledFunction10[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
68+
tupledImpl = (f: F) => f.asInstanceOf[Function10[_, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
69+
untupledImpl = (g: G) =>
70+
((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any) =>
71+
g.asInstanceOf[Tuple10[_, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10))).asInstanceOf[F]
72+
)
73+
74+
def tupledFunction11[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
75+
tupledImpl = (f: F) => f.asInstanceOf[Function11[_, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
76+
untupledImpl = (g: G) =>
77+
((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any) =>
78+
g.asInstanceOf[Tuple11[_, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11))).asInstanceOf[F]
79+
)
80+
81+
def tupledFunction12[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
82+
tupledImpl = (f: F) => f.asInstanceOf[Function12[_, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
83+
untupledImpl = (g: G) =>
84+
((x1: Any, x2: Any, x3: Any, x4: Any, x5: Any, x6: Any, x7: Any, x8: Any, x9: Any, x10: Any, x11: Any, x12: Any) =>
85+
g.asInstanceOf[Tuple12[_, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12))).asInstanceOf[F]
86+
)
87+
88+
def tupledFunction13[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
89+
tupledImpl = (f: F) => f.asInstanceOf[Function13[_, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
90+
untupledImpl = (g: G) =>
91+
((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) =>
92+
g.asInstanceOf[Tuple13[_, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13))).asInstanceOf[F]
93+
)
94+
95+
def tupledFunction14[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
96+
tupledImpl = (f: F) => f.asInstanceOf[Function14[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
97+
untupledImpl = (g: G) =>
98+
((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) =>
99+
g.asInstanceOf[Tuple14[_, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14))).asInstanceOf[F]
100+
)
101+
102+
def tupledFunction15[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
103+
tupledImpl = (f: F) => f.asInstanceOf[Function15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
104+
untupledImpl = (g: G) =>
105+
((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) =>
106+
g.asInstanceOf[Tuple15[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15))).asInstanceOf[F]
107+
)
108+
109+
def tupledFunction16[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
110+
tupledImpl = (f: F) => f.asInstanceOf[Function16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
111+
untupledImpl = (g: G) =>
112+
((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) =>
113+
g.asInstanceOf[Tuple16[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16))).asInstanceOf[F]
114+
)
115+
116+
def tupledFunction17[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
117+
tupledImpl = (f: F) => f.asInstanceOf[Function17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
118+
untupledImpl = (g: G) =>
119+
((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) =>
120+
g.asInstanceOf[Tuple17[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] => Any].apply((x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17))).asInstanceOf[F]
121+
)
122+
123+
def tupledFunction18[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
124+
tupledImpl = (f: F) => f.asInstanceOf[Function18[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
125+
untupledImpl = (g: G) =>
126+
((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) =>
127+
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]
128+
)
129+
130+
def tupledFunction19[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
131+
tupledImpl = (f: F) => f.asInstanceOf[Function19[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
132+
untupledImpl = (g: G) =>
133+
((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) =>
134+
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]
135+
)
136+
137+
def tupledFunction20[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
138+
tupledImpl = (f: F) => f.asInstanceOf[Function20[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
139+
untupledImpl = (g: G) =>
140+
((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) =>
141+
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]
142+
)
143+
144+
def tupledFunction21[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
145+
tupledImpl = (f: F) => f.asInstanceOf[Function21[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
146+
untupledImpl = (g: G) =>
147+
((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) =>
148+
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]
149+
)
150+
151+
def tupledFunction22[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
152+
tupledImpl = (f: F) => f.asInstanceOf[Function22[_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _]].tupled.asInstanceOf[G],
153+
untupledImpl = (g: G) =>
154+
((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) =>
155+
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]
156+
)
157+
158+
def tupledFunctionXXL[F, G]: TupledFunction[F, G] = TupledFunction[F, G](
159+
tupledImpl = (f: F) => ((args: TupleXXL) => f.asInstanceOf[FunctionXXL].apply(args.elems)).asInstanceOf[G],
160+
untupledImpl = (g: G) => new FunctionXXL {
161+
override def apply(xs: IArray[Object]): AnyRef = g.asInstanceOf[TupleXXL => AnyRef].apply(TupleXXL.fromIArray(xs))
162+
}.asInstanceOf[F]
163+
)
164+
165+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package scala.util
2+
3+
import scala.annotation.implicitNotFound
4+
import scala.annotation.experimental
5+
6+
/** Type class relating a `FunctionN[..., R]` with an equivalent tupled function `Function1[TupleN[...], R]`
7+
*
8+
* @tparam F a function type
9+
* @tparam G a tupled function type (function of arity 1 receiving a tuple as argument)
10+
*/
11+
@implicitNotFound("${F} cannot be tupled as ${G}")
12+
@experimental
13+
sealed trait TupledFunction[F, G]:
14+
def tupled(f: F): G
15+
def untupled(g: G): F
16+
17+
@experimental
18+
private[scala] object TupledFunction:
19+
def apply[F, G](tupledImpl: F => G, untupledImpl: G => F): TupledFunction[F, G] =
20+
new TupledFunction[F, G]:
21+
def tupled(f: F): G = tupledImpl(f)
22+
def untupled(g: G): F = untupledImpl(g)

0 commit comments

Comments
 (0)