Skip to content

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

Closed
@justinhj

Description

@justinhj

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions