Hello,

In the conext of my master semester project, I’m implementing Polymorphic eta-expansion, I would like to have your feedback!

### What it is:

Polymorphic eta-expansion is the step of (automatically) converting a polymorphic method, such as:

```
def id[T](x: T) = x
// type of id: [T](T): T
```

To a value of polymorphic function type, such as:

```
[T] => (x: T) => ident[T](x)
// type: [T] => T => T
```

The full technical details can be found it this PR to the scala 2 doc (as there is no scala 3 doc yet):

Polymorphic eta-expansion allows us to write shorter, clearer code by removing boilerplate:

### Simple Examples:

(taken from the new test file)

```
// Type [T] => T => T, used to be Any => Any
val valId /*[T] => T => T*/ = id
val valValId: [T] => T => T = valId1
// recall (tu: Tuple).map[F[_]](f: [t] => (x$1: t) => F[t]): Map[Tuple, F]
val mapped: (Int, String, Char) = (1, "two", '3').map(id)
val optioner: [T] => T => Option[T] = Option.apply
type Z
type X <: Z
type Y >: X <: Z
val valIdxyz: [T >: X <: Y ] => X => Z = id
def monoPair[T](x: T)(y: T): (T, T) = (x, y)
val valMonoPair: [T] => T => T => (T,T) = monoPair
def protoPair[T](x: T): [U] => (U) => (T, U) = [U] => (y: U) => (x,y)
val valProtoPair1: [T] => (T) => [U] => U => (T, U) = protoPair
```

(The change from `valId`

having type `Any => Any`

to having `[T] => T => T`

should not break anything, as `valId`

can refer to `valId[Any]`

throught type Inferrence.)

### Real-world examples:

In this part of the shapeless library, the explicit conversion at line 511 can be replaced by the more succint:

```
// before:
given Pure[Id] = mkPure([T] => (t: T) => t)
// now:
given Pure[Id] = mkPure(identity)
```

Another similar case can be found at line 244 (`G.pure`

)

In the neighboring file `instances.scala`

, no obvious example can be found, there is however code duplication of:

```
[t] => (tc: TypeClass[t]) => tc.another
```

Which given a function like:

```
def takeAnother[T](tc: TypeClass[T]) = tc.another
```

Could be replaced, for example in this line:

```
val otherInst = inst.mapK(takeAnother)
```

### Side Effects:

It was not possible to write something like the following: (works if `() =>`

is inserted)

```
val noneFun: [T] => Option[T] = [T] => None
```

Fortunately, there is a PR to address this exact problem, so I rebased on top of:

Unfortunately, this brings the "`[T] => (T => T)`

is not the same as `[T] => (T) => T`

" problem

To fix it, all poly eta-expanded methods are brought to a common, unique form: `PolyFunction{def apply[<tArgs>]: <ret>}`

where `<ret>`

in the above would be `T => T`

.

(Unlike before, where `apply`

could have more than one parameter clause)

As a consequence:

- all polymorphic values erase to
`Function0`

(except manual creation of`PolyFunction`

subclasses) - tasty backward compatibility is broken

### Ways in which it could get extended:

Find a way to make the new `PolyFunction`

behaviour as backwards compatible as possible, @smarter had some ideas and might be working on it in the future

Keep “universality” of wildcard:

```
val fromWildcard: [T] => T => T = identitiy(_)
```

Keep “universality” of lambdas without type ascription:

```
val fromLambda: [T] => T => T = y => y
```

Create type lambdas through wildcard:

```
val fromWildcardType: [T] => T => T = identitiy[_](_)
```

Would be particularly useful in cases where there is more than one type parameter, which is not particularly frequent, but could be:

### Parallel project: Interweaved Clauses

If this and interweaved clauses (forum, doc) are added, the following should work without any additions:

```
def pair[T](x: T)[U](y: U) = (x, y) //method type [T](T)[U](U): (T, U)
val valPair: [T] => T => [U] => U => (T,U) = pair
val pairFromSecond: [U] => U => (Int, U) = pair(42)
```

### Conclusion

I hope you find this feature useful, and am happy to answer any questions,

I would appreciate feedback as this is my first language contribution to scala !