Skip to content

Erroneous ambiguous extension methods error when working with tuples #14165

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

Open
mpilquist opened this issue Dec 23, 2021 · 0 comments
Open

Erroneous ambiguous extension methods error when working with tuples #14165

mpilquist opened this issue Dec 23, 2021 · 0 comments

Comments

@mpilquist
Copy link
Contributor

mpilquist commented Dec 23, 2021

Compiler version

3.1.1-RC1

Minimized code

// using scala 3.1.1-RC1

trait Applicative[F[_]]:
  def pure[A](a: A): F[A]

  extension [A](fa: F[A])
    def map[B](f: A => B): F[B] = fa.map2(pure(()))((a, _) => f(a))
    def map2[B, C](fb: F[B])(f: (A, B) => C): F[C]

  extension [T <: NonEmptyTuple : Tuple.IsMappedBy[F]](t: T)
    def mapN[B](f: Tuple.InverseMap[T, F] => B): F[B] =
      t.tupled.map(f)

    def tupled: F[Tuple.InverseMap[T, F]] =
      def loop[X <: NonEmptyTuple](x: X): F[Tuple] = x match
        case hd *: EmptyTuple => hd.asInstanceOf[F[Any]].map(_ *: EmptyTuple)
        case hd *: (tl: NonEmptyTuple) => hd.asInstanceOf[F[Any]].map2(loop(tl))(_ *: _)
      loop(t).asInstanceOf[F[Tuple.InverseMap[T, F]]]

given optionApplicative: Applicative[Option] with
  def pure[A](a: A) = Some(a)
  extension [A](fa: Option[A])
    def map2[B, C](fb: Option[B])(f: (A, B) => C) = 
      fa.flatMap(a => fb.map(b => f(a, b)))

given listApplicative: Applicative[List] with
  def pure[A](a: A) = List(a)
  extension [A](fa: List[A])
    def map2[B, C](fb: List[B])(f: (A, B) => C) = 
      fa.flatMap(a => fb.map(b => f(a, b)))

@main def applicatives =
  println((Option(1), Option(2), Option(3)).tupled)
  println((Option(1), Option(2), Option(3)).mapN(_ + _ + _))

Further minimization:

// using scala 3.1.1-RC1

trait Applicative[F[_]]:
  extension [T <: NonEmptyTuple : Tuple.IsMappedBy[F]](t: T)
    def mapN[B](f: Tuple.InverseMap[T, F] => B): F[B] = ???

given optionApplicative: Applicative[Option] with {}
given listApplicative: Applicative[List] with {}

@main def applicatives =
  (Option(1), Option(2), Option(3)).mapN(_ + _ + _)

Output

println((Option(1), Option(2), Option(3)).tupled) works fine but println((Option(1), Option(2), Option(3)).mapN(_ + _ + _)) fails to compile with:

[error] ./app.scala:34:11: Found:    (Option[Int], Option[Int], Option[Int])
[error] Required: ?{ mapN: ? }
[error] Note that implicit extension methods cannot be applied because they are ambiguous;
[error] both object optionApplicative and object listApplicative provide an extension method `mapN` on (Option[Int], Option[Int], Option[Int])
[error]   println((Option(1), Option(2), Option(3)).mapN(_ + _ + _))
[error]           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expectation

The listApplicative instance should not conflict with optionApplicative instance because the mapN method from the list instance requires Tuple.IsMappedBy[List] evidence.

Moving the Tuple.IsMappedBy[F] constraint from the extension declaration down to each extension method and ensuring the using param list on mapN comes before the regular param list yields the expected behavior. That is, the following works fine:

trait Applicative[F[_]]:
  extension [T <: NonEmptyTuple](t: T)
    def mapN[B](using Tuple.IsMappedBy[F][T])(f: Tuple.InverseMap[T, F] => B): F[B] = ???

Whereas this version has the same ambiguity error as the original:

trait Applicative[F[_]]:
  extension [T <: NonEmptyTuple](t: T)
    def mapN[B](f: Tuple.InverseMap[T, F] => B)(using Tuple.IsMappedBy[F][T]): F[B] = ???

Seems similar to #12126.

@ckipp01 ckipp01 added the stat:needs triage Every issue needs to have an "area" and "itype" label label May 15, 2023
@nicolasstucki nicolasstucki added area:extension-methods area:tuples and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jan 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants