diff --git a/docs/docs/reference/auto-parameter-tupling-spec.md b/docs/docs/reference/auto-parameter-tupling-spec.md new file mode 100644 index 000000000000..9381300a23fa --- /dev/null +++ b/docs/docs/reference/auto-parameter-tupling-spec.md @@ -0,0 +1,85 @@ +--- +layout: doc-page +title: "Automatic Tupling of Function Parameters - More Details" +--- + +### Motivation + +Say you have a list of pairs + +```scala +val xs: List[(Int, Int)] +``` + +and you want to map `xs` to a list of `Int`s so that each pair of numbers is mapped to their sum. +Previously, the best way to do this was with a pattern-matching decomposition: +```scala +xs.map { + case (x, y) => x + y +} +``` +While correct, this is inconvenient. Instead, we propose to write it the following way: + +```scala +xs.map { + (x, y) => x + y +} +``` +or, equivalently: +```scala +xs.map(_ + _) +``` + +Generally, a function value with `n > 1` parameters can be converted to a function with tupled arguments if the expected type is a unary function type of the form `((T_1, ..., T_n)) => U`. + +### Type Checking + +Let a function `f` of the form `(p1, ..., pn) => e` for `n != 1`, parameters `p1, ..., pn`, and an expression `e`. + +If the expected type of `f` is a fully defined function type or SAM-type that has a +single parameter of a subtype of `ProductN[T1, ..., Tn]`, where each type `Ti` fits the corresponding +parameter `pi`. Then `f` will conform to the function type `ProductN[T1, ..., Tn] => R`. + +A type `Ti` fits a parameter `pi` if one of the following two cases is `true`: + +* `pi` comes without a type, i.e. it is a simple identifier or `_`. +* `pi` is of the form `x: Ui` or `_: Ui` and `Ti` conforms to `Ui`. + +Auto-tupling composes with eta-expansion. That is an n-ary function generated by eta-expansion +can in turn be adapted to the expected type with auto-tupling. + +#### Term addaptation + +If the a function +```scala +(p1: T1, ..., pn: Tn) => e +``` + +is typed as `ProductN[T1, ..., Tn] => Te`, then it will be transformed to + +```scala +(x: TupleN[T1, ..., Tn]) => { + def p1: T1 = x._1 + ... + def pn: Tn = x._n + e +} +``` + +##### Generic tuples + +If we come to support generic tuples, which provide the possibility of having tuples/functions of arities larger than 22 we would need to additionally support generic tuples of the form `T1 *: T2 *: ...`. +Translation of such a tuples would use the `apply` method on the tuple to access the elements instead of the `_N` methods of `Product`. + +### Migration + +Code like this could not be written before, hence the new notation would not be ambigouous after adoption. + +Though it is possible that someone has written an implicit conversion form `(T1, ..., Tn) => R` to `TupleN[T1, ..., Tn] => R` +for some `n`. This change could be detected and fixed by `Scalafix`. Furthermore, such conversion would probably +be doing the same translation (semantically) but in a less efficient way. + +### Reference + +For more info see: +* [Issue #897](https://github.com/lampepfl/dotty/issues/897). diff --git a/docs/docs/reference/auto-parameter-tupling.md b/docs/docs/reference/auto-parameter-tupling.md index fdfdee233ad3..4961d3e59db3 100644 --- a/docs/docs/reference/auto-parameter-tupling.md +++ b/docs/docs/reference/auto-parameter-tupling.md @@ -30,4 +30,6 @@ function type of the form `((T_1, ..., T_n)) => U`. ### Reference -For more info, see [Issue #897](https://github.com/lampepfl/dotty/issues/897). +For more info see: +* [More details](./auto-parameter-tupling-spec.html) +* [Issue #897](https://github.com/lampepfl/dotty/issues/897). diff --git a/tests/neg/automatic-tupling-of-function-parameters.scala b/tests/neg/automatic-tupling-of-function-parameters.scala new file mode 100644 index 000000000000..c7d01be78856 --- /dev/null +++ b/tests/neg/automatic-tupling-of-function-parameters.scala @@ -0,0 +1,7 @@ + +object Test { + + val f1: Tuple1[Int] => Int = (x: Int) => x // error + val g1: Tuple1[Int] => Int = _ // error + +} diff --git a/tests/pos/automatic-tupling-of-function-parameters.scala b/tests/pos/automatic-tupling-of-function-parameters.scala new file mode 100644 index 000000000000..2afc7eaf10e3 --- /dev/null +++ b/tests/pos/automatic-tupling-of-function-parameters.scala @@ -0,0 +1,39 @@ + +object Test { + + val f2: Tuple2[Int, String] => Int = (x, y) => x + val g2: Tuple2[Int, String] => Int = _ + _.length + + // FIXME issue #5257 +// val h2: Int *: Int *: Unit => Int = (x, y) => x + y +// val k2: Int *: Tuple1[Int] => Int = (x, y) => x + y + + type T2 = Tuple2[Int, Int] + val h2: T2 => Int = (x1, x2) => 2 + + val f3: Tuple3[Int, Int, Int] => Int = (x1, x2, x3) => 3 + val g3: Tuple3[Int, Int, Int] => Int = _ + _ + _ + + val f5: Tuple5[Int, Int, Int, Int, Int] => Int = (x1, x2, x3, x4, x5) => 5 + val g5: Tuple5[Int, Int, Int, Int, Int] => Int = _ + _ + _ + _ + _ + + val f10: Tuple10[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int] => Int = + (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) => 10 + val g10: Tuple10[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int] => Int = + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + val f22: Tuple22[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int] => Int = + (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22) => 22 + val g22: Tuple22[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int] => Int = + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + + // FIMXE Tuples of size larger that 22 are not supported yet (issue #5256) +// val f23: ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => Int = +// (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23) => 22 +// val g23: ((Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)) => Int = +// _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + _ + +// type T23 = (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) +// val h23: T23 => Int = (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23) => 23 + +} diff --git a/tests/run/automatic-tupling-of-function-parameters-implicit-conversion.scala b/tests/run/automatic-tupling-of-function-parameters-implicit-conversion.scala new file mode 100644 index 000000000000..6e963225eb6d --- /dev/null +++ b/tests/run/automatic-tupling-of-function-parameters-implicit-conversion.scala @@ -0,0 +1,14 @@ + +object Test { + + implicit def F2ToT2(f: Function2[Int, Int, Int]): Tuple2[Int, Int] => Int = { + ??? + x => f(x._1, x._2) + } + + def main(args: Array[String]): Unit = { + val f: Tuple2[Int, Int] => Int = (x: Int, y: Int) => x + y + f((3, 4)) + } + +} diff --git a/tests/run/automatic-tupling-of-function-parameters-spec.check b/tests/run/automatic-tupling-of-function-parameters-spec.check new file mode 100644 index 000000000000..a0f16194ddb4 --- /dev/null +++ b/tests/run/automatic-tupling-of-function-parameters-spec.check @@ -0,0 +1,3 @@ +List(3, 7, 11) +List(3, 7, 11) +List(3, 7, 11) diff --git a/tests/run/automatic-tupling-of-function-parameters-spec.scala b/tests/run/automatic-tupling-of-function-parameters-spec.scala new file mode 100644 index 000000000000..0ee93420064c --- /dev/null +++ b/tests/run/automatic-tupling-of-function-parameters-spec.scala @@ -0,0 +1,18 @@ + +object Test { + + def main(args: Array[String]): Unit = { + val xs: List[(Int, Int)] = (1, 2) :: (3, 4) :: (5, 6) :: Nil + + println(xs.map { + case (x, y) => x + y + }) + + println(xs.map { + (x, y) => x + y + }) + + println(xs.map(_ + _)) + } + +}