question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Problems inferring correct type for extension methods

See original GitHub issue

Compiler version

3.0.0-RC1

Minimized code

object Minimized extends App {
  
  trait Monad[F[_]]:
    def pure[A](a: A): F[A]
    
    extension[A,B](fa :F[A]) 
      def map(f: A => B): F[B] = fa.flatMap(a => pure(f(a)))
      def flatMap(f :A=>F[B]):F[B]
  end Monad

  // Instances of monad
 // note that if you comment out eitherMonad the code will compile, even though the Either type is never used 
  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
        }
      }

  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] = summon[Monad[F]].flatMap(fa.wrapped)(a => f(a).wrapped)
        Transformer(ffa)
      }
  }
  
  type TransformerOption[A] = Transformer[Option, A]
  
  val pure10 = summon[Monad[TransformerOption]].pure(10)
  val fm = pure10.flatMap(a => Transformer(Option(a + 1))) // COMPILE ERROR HERE
  println(fm)
}

Output

Minimized.scala:49:19
value flatMap is not a member of Minimized.Transformer[Option, Int].
An extension method was tried, but could not be fully constructed:

    Minimized.transformerMonad[F](
      /* ambiguous: both given instance eitherMonad in object Minimized and object optionMonad in object Minimized match type Minimized.Monad[F] */
        summon[Minimized.Monad[F]]
    ).flatMap()
  val fm = pure10.flatMap(a => Transformer(Option(a + 1)))

Expectation

Code should compile with no ambiguity

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
prolativcommented, Mar 10, 2021

Here

pure10.flatMap(a => Transformer(Option(a + 1)))

expands to

transformerMonad.flatMap(pure10)(a => Transformer(Option(a + 1)))

and at this point compiler is confused while at the point of evaluating transformerMonad both transformerMonad[Option] and transformerMonad[Either] make sense (the compiler doesn’t know yet that you’re in the context of Option rather than Either) so it’s not clear which implicit should be taken. You can try adding some kind of extension proxy to make this to make your snippet compile

extension[F[_], A](fa: F[A])(using monad: Monad[F])
  def flatMap[B](f: A => F[B]) = monad.flatMap(fa)(f)
0reactions
justinhjcommented, Mar 12, 2021

Thanks for the help, so it seems like this isn’t a bug and can be closed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

VS Code incorrectly infers types from extension methods
Specifying the type explicitly works, but that's rather a lot of typing, and this should be simple enough. It looks to me like...
Read more >
Type inference in extension methods #6275 - GitHub
I'd like to add an extension method that has a type parameter with a constraint as well that also relies on other type...
Read more >
Extension methods and Generics (Extension Methods Part 5)
Attempting to resolve all types in one pass, the compiler would infer two conflicting types for T3, one from parameter X and another...
Read more >
Why I Don't Like C# Extension Methods - DaedTech
You can make it look as though GetWordCount() is a method of string, even though it isn't. This means that, done right, you...
Read more >
Extension methods - Dart
Extension methods do work with Dart's type inference. The following code is fine because the variable v is inferred to have type String...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found