Skip to content

Extension method could not be constructed due to ambiguous methods #11243

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

Closed
justinhj opened this issue Jan 29, 2021 · 4 comments · Fixed by #11274
Closed

Extension method could not be constructed due to ambiguous methods #11243

justinhj opened this issue Jan 29, 2021 · 4 comments · Fixed by #11274

Comments

@justinhj
Copy link

justinhj commented Jan 29, 2021

Compiler version

3.0.0-M3

Minimized code

In the code sample, I've implemented a simple Monad transformer called Transformer that wraps another Monad. For some reason the internal Monad[F] is ambiguous and the code does not compile. I pretty much ported this from Scala 2.x code, so I'm not sure if the problem is my misunderstanding or a bug in the typer.

object WriterTest extends App {
  
  object Functor:
    def apply[F[_]](using f: Functor[F]) = f

  trait Functor[F[_]]:
    extension [A, B](x: F[A])
      def map(f: A => B): F[B]
  
  object Applicative:
    def apply[F[_]](using a: Applicative[F]) = a

  trait Applicative[F[_]] extends Functor[F]:
    def pure[A](x:A):F[A]

    extension [A,B](x: F[A])
      def ap(f: F[A => B]): F[B]
  
      def map(f: A => B): F[B] = {
        x.ap(pure(f))
      }

    extension [A,B,C](fa: F[A]) def map2(fb: F[B])(f: (A,B) => C): F[C] = {
      val fab: F[B => C] = fa.map((a: A) => (b: B) => f(a,b))
      fb.ap(fab)
    }

  end Applicative


  object Monad:
    def apply[F[_]](using m: Monad[F]) = m

  trait Monad[F[_]] extends Applicative[F]:

    // The unit value for a monad
    def pure[A](x:A):F[A]

    extension[A,B](fa :F[A])
      // The fundamental composition operation
        def flatMap(f :A=>F[B]):F[B]
  
        // Monad can also implement `ap` in terms of `map` and `flatMap`
        def ap(fab: F[A => B]): F[B] = {
          fab.flatMap {
            f =>
              fa.flatMap {
                a =>
                  pure(f(a))
              }
          }
  
        }

  end Monad

  given eitherMonad[Err]: Monad[[X] =>> Either[Err,X]] with
    def pure[A](a: A): Either[Err, A] = Right(a)
    extension [A,B](x: Either[Err,A]) def flatMap(f: A => Either[Err, B]) = {
      x match {
        case Right(a) => f(a)
        case Left(err) => Left(err)
      }
    }

  given optionMonad: Monad[Option] with
    def pure[A](a: A) = Some(a)
    extension[A,B](fa: Option[A])
      def flatMap(f: A => Option[B]) = {
        fa match {
          case Some(a) =>
            f(a)
          case None =>
            None
        }
      }

  given listMonad: Monad[List] with
    def pure[A](a: A): List[A] = List(a)
  
    extension[A,B](x: List[A])
      def flatMap(f: A => List[B]): List[B] = {
        x match {
          case hd :: tl => f(hd) ++ tl.flatMap(f)
          case Nil => Nil
        }
      }

  case class Transformer[F[_]: Monad,A](val wrapped: F[A])
  
  given transformerMonad[F[_]: Monad]: Monad[[X] =>> Transformer[F,X]] with {

    def pure[A](a: A): Transformer[F,A] = Transformer(summon[Monad[F]].pure(a))

    extension [A,B](fa: Transformer[F,A]) 
      def flatMap(f: A => Transformer[F,B]) = {
        val ffa: F[B] = Monad[F].flatMap(fa.wrapped) {
          case a => {
            f(a).wrapped.map {
              case b =>
                b
            }
          }
        }
        Transformer(ffa)
      }
  }
  
  type EString[A] = Either[String,A]

  def incrementEven(a: Int): Transformer[EString,Int] = {
    if(a % 2 == 1) Transformer(Left("Odd number provided"))
    else Transformer(Right(a + 1))
  }

  def doubleOdd(a: Int): Transformer[EString, Int] = {
    if(a % 2 == 0) Transformer(Left("Even number provided"))
    else Transformer(Right(a * 2))
  }
  
  val writerExample = incrementEven(8)
  val example = writerExample.flatMap(doubleOdd) // Error ambiguous F


}

Output

  /*
  /Users/justinhj/evalexample/src/main/scala/WriterTest.scala:123:31
value flatMap is not a member of WriterTest.Transformer[WriterTest.EString, Int].
An extension method was tried, but could not be fully constructed:

    WriterTest.transformerMonad[F](
      /* ambiguous: both method eitherMonad in object WriterTest and object optionMonad in object WriterTest match type WriterTest.Monad[F] */
        summon[WriterTest.Monad[F]]
    ).flatMap()
   */

Expectation

Should be able to use the transformerMonad method to create a given instance.

For reference here is the sample code in Scala 2.x
https://gist.github.com/justinhj/79f223bfb75791d40a6a5fd431ad86ea

The monad is WriterT rather than the simplified Transformer here.

@odersky
Copy link
Contributor

odersky commented Jan 29, 2021

Did you try to complete the example by hand i.e. first

WriterTest.transformerMonad[F].flatMap```

and then

WriterTest.transformerMonad[F](/*provide right monad here*/).flatMap```

what did you get?

@justinhj
Copy link
Author

That was fruitful, thanks! Well, it seems I can use the Monad when completing by hand. Both pure and flatMap are good, but the problem is in the last line where I try to use the extension method it has trouble finding the right type...

  val a1: Transformer[EString,Int] = WriterTest.transformerMonad[EString].pure(10)
  println(a1)

  val a2 = WriterTest.transformerMonad[EString].flatMap(a1)(incrementEven)
  println(a2)
  // Transformer(Right(10))

  println(a2.flatMap(doubleOdd))
  // Transformer(Right(11))

   println(a2.flatMap(doubleOdd))

First two println's work but when adding the last line I get the error...

value flatMap is not a member of WriterTest.Transformer[WriterTest.EString, Int].
An extension method was tried, but could not be fully constructed:

    WriterTest.transformerMonad[F](
      /* ambiguous: both method eitherMonad in object WriterTest and object optionMonad in object WriterTest match type WriterTest.Monad[F] */
        summon[WriterTest.Monad[F]]
    ).flatMap()
  println(a2.flatMap(doubleOdd))

@odersky
Copy link
Contributor

odersky commented Jan 31, 2021

I just tried it myself:

WriterTest.transformerMonad.flatMap(writerExample)(doubleOdd)

gives the ambiguity error but

WriterTest.transformerMonad[EString].flatMap(writerExample)(doubleOdd)

is OK. The problem here is that at the point where the ambiguity is raised we have not analyzed doubleOdd yet, so we have not constrained F to be EString.

odersky added a commit to dotty-staging/dotty that referenced this issue Feb 1, 2021
When retrying after an ambiguous implicit, we now make use of all the
information in the prototype, including ignored parts. We also try
to match formal parameters with actually given arguments.

Fixes scala#11243
Fixes scala#5773, which previously was closed with a more detailed error message.
odersky added a commit to dotty-staging/dotty that referenced this issue Feb 1, 2021
When retrying after an ambiguous implicit, we now make use of all the
information in the prototype, including ignored parts. We also try
to match formal parameters with actually given arguments.

Fixes scala#11243
Fixes scala#5773, which previously was closed with a more detailed error message.
@justinhj
Copy link
Author

justinhj commented Feb 1, 2021

That's great, thanks! I'm glad it was not just a simple user error.

odersky added a commit to dotty-staging/dotty that referenced this issue Feb 1, 2021
When retrying after an ambiguous implicit, we now make use of all the
information in the prototype, including ignored parts. We also try
to match formal parameters with actually given arguments.

Fixes scala#11243
Fixes scala#5773, which previously was closed with a more detailed error message.
michelou pushed a commit to michelou/scala3 that referenced this issue Feb 5, 2021
When retrying after an ambiguous implicit, we now make use of all the
information in the prototype, including ignored parts. We also try
to match formal parameters with actually given arguments.

Fixes scala#11243
Fixes scala#5773, which previously was closed with a more detailed error message.
@Kordyjan Kordyjan added this to the 3.0.0 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants