scala3 icon indicating copy to clipboard operation
scala3 copied to clipboard

Dealias type when type parameters inference occurs

Open Alex1005a opened this issue 6 months ago • 1 comments

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.

Alex1005a avatar May 22 '25 19:05 Alex1005a

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.

odersky avatar Jun 05 '25 11:06 odersky

@smarter @som-snytt Have you maybe managed to take a look?

Gedochao avatar Aug 27 '25 11:08 Gedochao

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...

som-snytt avatar Aug 27 '25 13:08 som-snytt

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

smarter avatar Aug 27 '25 19:08 smarter

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?

Alex1005a avatar Oct 30 '25 16:10 Alex1005a

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?

Alex1005a avatar Oct 30 '25 16:10 Alex1005a