Skip to content

Commit

Permalink
Revert "Remove TupledFunction"
Browse files Browse the repository at this point in the history
This reverts commit f4e1ca4.

Also makes TupledFunction experimental
  • Loading branch information
nicolasstucki committed Feb 28, 2022
1 parent 8922c88 commit 64cee08
Show file tree
Hide file tree
Showing 22 changed files with 330 additions and 6 deletions.
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Expand Up @@ -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")
Expand Down
45 changes: 45 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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,
Expand Down
82 changes: 82 additions & 0 deletions 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))
}
```
1 change: 1 addition & 0 deletions docs/sidebar.yml
Expand Up @@ -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
Expand Down
165 changes: 165 additions & 0 deletions 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]
)

}
22 changes: 22 additions & 0 deletions 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)

0 comments on commit 64cee08

Please sign in to comment.