Dealias type when type parameters inference occurs
The code below displays the type parameter [Res] =>> TypeAl[F, String, Res] for the toFunctorOps implicit conversion.
import cats.Functor
import cats.syntax.all.*
type Result[F[_], Err, Res] = F[Either[Err, Res]]
def test[F[_]: Functor](x: Result[F, String, Int]) = {
x.map(identity) // error here
}
To fix this, you can do dealias if the type before dealias does not have the same arity as the constructor with which unification occurs, but the type after dealias does have the same arity. Then the type parameter F will be inferred.
I think @smarter would be the best person to review this. Guillaume, can you take a look? You have thought most about dealiasing and type inference.
@smarter @som-snytt Have you maybe managed to take a look?
I'll learn something by taking a look. I recently removed dealiasing from Scala 2's typedNew and also contended in Scala 3 with the type of new A.C for C[T] where the unused check sees type [T] =>> this.A.C[T] at https://github.com/scala/scala3/pull/23699
Edit: apparently I already have a branch review/23242...
Sorry for the late reply! So having type aliases influence type inference here was a conscious choice so that users could customize higher-order unification by defining their own type aliases. The logic proposed in this PR is sound, but I worry it's making the rules around type inference more complex. An alternative here would be to use a curried type alias to get unification to do exactly what you want:
//> using dep org.typelevel::cats-core:2.13.0
import cats.Functor
import cats.syntax.all.*
type Result[Err, Res] = [F[_]] =>> F[Either[Err, Res]]
def test[F[_]: Functor](x: Result[String, Int][F]) =
x.map(identity) // ok
Thanks for the clarification. It seems like this syntax would be a bit confusing and exotic. Result[F, String, Int] would be much more understandable to the average Scala developer than Result[String, Int][F]. Furthermore, it's unclear when to use a curried type alias and when a regular one.
it's making the rules around type inference more complex.
Do you mean that this change requires changing some compiler internals or the theoretical foundations of type inference in Scala 3?
By the way, I noticed that if you make the F parameter the last one, the code compiles.
import cats.Functor
import cats.syntax.all.*
type Result[Err, Res, F[_]] = F[Either[Err, Res]]
def test[F[_]: Functor](x: Result[String, Int, F]) = {
x.map(identity) // ok
}
Is there any explanation for this?