Skip to content

Allow partial eta expansion #2691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 13, 2017
Merged

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Jun 6, 2017

Allow partial eta expansion using the following rule:

Assume we have an application

(x_1, ..., x_m) => f(<arg_1, ..., arg_n>)

where every x_i occurs exactly once as argument to f, and typing f with (?, ..., ?) => ?
as expected type (where there are n occurrences of ? in the argument list)
yields a method type (T_1, ..., T_n)R. Let p be the mapping from 1..m to 1..n
which maps every parameter x_i to the position where it occurs in <arg_1, ..., arg_n>.

In this case, if one of the parameter types x_i is not given and not inferred from the
expected type, assume T_p(i) as the type of x_i, provided T_p(i) is fully defined
and not a repeated type T* or =>T*.

The reason for excluding repeated types T* is that T* is not a valid type for a
lambda parameter.

Fixes one part of #2570.

Allow partial eta expansion using the following rule:

Assume we have an application

    (x_1, ..., x_m) => f(<arg_1, ..., arg_n>)

every `x_i` occurs exactly once as argument to `f`, and typing `f` with `(?, ..., ?) => ?`
as expected type (where there are n occurrences of `?` in the argument list)
yields a method type `(T_1, ..., T_n)R`. Let `p` be the mapping from `1..m` to `1..n`
which maps every parameter `x_i` to the position where it occurs in `<arg_1, ..., arg_n>`.

In this case, if one of the parameter types `x_i` is not given and not inferred from the
expected type, assume `T_p(i)` as the type of `x_i`, provided `T_p(i)` is fully defined
and not a repeated type `T*` or `=>T*`.

The reason for excluding repeated types `T*` is that `T*` is not a valid type for a
lambda parameter.
@odersky
Copy link
Contributor Author

odersky commented Jun 7, 2017

Reverted back to initial commit because the rest is not directly related (and more subtle, it turns out).

@odersky odersky mentioned this pull request Jun 7, 2017
Copy link
Contributor

@adriaanm adriaanm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Added a few questions / suggestions for clarification.

for (param <- params; idx <- paramIndices(param, args, 0))
yield param.name -> idx
}.toMap
if (paramIndex.size == params.length)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's a map, wouldn't this miss the (unlikely) case where the same param (one key) occurs in multiple args (multiple values, of which only the last one is retained). I don't think it's a big deal, as this could not arise from the _ syntax, but the other comments do mention "exactly once" as a requirement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The revised implementation of paramIndices makes sure that a parameter is entered in the map only if there is a unique occurrence in args.

case untpd.TypedSplice(expr1) =>
expr1.tpe
case _ =>
val protoArgs = args map (_ withType WildcardType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, the current implementation in Scala 2 includes the known argument types / result type, to enable overload resolution. Would be good to indicate the difference in a comment.

@@ -0,0 +1,29 @@
object Test {

def repeat(s: String, i: Int, j: Int = 22) = s * i
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When discussing this with Seth, he suggested to play around with currying by adding another argument list. The partial application should be fine: repeat(_, 1), but what about repeat(_, 1)(_)? (uncurrying?)

Copy link
Contributor Author

@odersky odersky Jun 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In dotc you get:

1 |repeat(1, _)(_)
  |             ^
  |             unbound placeholder parameter; incorrect use of `_`

In scalac you get:

scala> repeat(1, _)(_) 
<console>:13: error: missing parameter type for expanded function ((x$1: <error>, x$2) => repeat(1, x$1)(x$2))
       repeat(1, _)(_)
                 ^
<console>:13: error: missing parameter type for expanded function ((x$1: <error>, x$2: <error>) => repeat(1, x$1)(x$2))
       repeat(1, _)(_)

The fact that the types already contain <error> markers makes me suspect that scalac also issues a parsing error but somehow suppresses the error message.

@odersky
Copy link
Contributor Author

odersky commented Jun 13, 2017

I made a note to add the missing comment about differences between dotc and scalsc in the followup PR

@odersky odersky merged commit bf22702 into scala:master Jun 13, 2017
@allanrenucci allanrenucci deleted the fix-#2570-1 branch December 14, 2017 19:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants