rssh/dotty-cps-async

CpsMonad for type alias broke from 0.3.5 to 0.4.0

rcano opened this issue · 12 comments

rcano commented

In 0.3.5 I could have a CpsMonad for an alias type:

type MyMonad[T] = MyActualType

given cps.CpsMonad[MyMonad] with {...}

and this worked just fine. In 0.4.0 this broke, and now I get

java.lang.RuntimeException: Can't find async monad for [T >: scala.Nothing <: scala.Any] => MyActualType (transformImpl)
  | => sat cps.Async$.transformImpl(Async.scala:56)

Not sure how to work around this. I am aware that this is in some ways stretching the functionality, but it worked just fine in the previous version so perhaps there's hope :)

rssh commented

Will look. Can you attach a small self-contained test case? (file or small sbt project without extra dependencies is ok)
(it can be a reincarnation of scala/scala3#11479 , but need to look at the example to say something definitely)

rcano commented

Will do, in a couple of hours

rcano commented

Here's a way to reproduce it, note that I did find a work around by turning my type opaque

object BugRepro {
  object UsedToCompile {
    object MyType
    type PseudoMonad[T] =  MyType.type

    given myCpsMonad: cps.CpsMonad[PseudoMonad] with {
      def flatMap[A, B](fa: PseudoMonad[A])(f: A => PseudoMonad[B]): PseudoMonad[B] = MyType
      def map[A, B](fa: PseudoMonad[A])(f: A => B): PseudoMonad[B] = MyType
      def pure[T](t: T): PseudoMonad[T] = MyType
    }

    cps.async(using myCpsMonad) {
      cps.await(MyType)
    }
  }
  object StillCompiles {
    object MyType
    object opaques {
      opaque type PseudoMonad[T] =  MyType.type
    } 
    type PseudoMonad[T] = opaques.PseudoMonad[T]
    given [T]: Conversion[PseudoMonad[T], MyType.type] = _.asInstanceOf[MyType.type]
    given [T]: Conversion[MyType.type, PseudoMonad[T]] = _.asInstanceOf[PseudoMonad[T]]

    given myCpsMonad: cps.CpsMonad[PseudoMonad] with {
      def flatMap[A, B](fa: PseudoMonad[A])(f: A => PseudoMonad[B]): PseudoMonad[B] = MyType
      def map[A, B](fa: PseudoMonad[A])(f: A => B): PseudoMonad[B] = MyType
      def pure[T](t: T): PseudoMonad[T] = MyType
    }

    cps.async(using myCpsMonad) {
      cps.await(MyType)
    }
  }
}
rcano commented

Actually, hold on, I don't think the above accurately captures my problem, because in the above example, if I do cps.await[PseudoMonad, Any(MyType)] then it still compiles. I think there's another layer that I'm doing that's causing the issue.

rssh commented

Ok, thanks.
(During waiting the problem example, let's look on current test-case:

[error] -- [E007] Type Mismatch Error: /Users/rssh/work/oss/dotty-cps/dotty-cps/shared/src/test/scala/cps/TestIssue32.scala:13:16 
[error] 13 |      cps.await(MyType)
[error]    |                ^^^^^^
[error]    |Found:    BugRepro.UsedToCompile.MyType.type
[error]    |Required: ([_$1] =>> Any)[T]
[error]    |
[error]    |where:    T is a type variable with constraint 
[error]    |
[error]    |Note that implicit conversions cannot be applied because they are ambiguous;
[error]    |both method ArrowAssoc in object Predef and method Ensuring in object Predef convert from BugRepro.UsedToCompile.MyType.type to ([_$1] =>> Any)[T]
[error] -- Error: /Users/rssh/work/oss/dotty-cps/dotty-cps/shared/src/test/scala/cps/TestIssue32.scala:13:23 
[error] 13 |      cps.await(MyType)
[error]    |                       ^
[error]    |no implicit argument of type cps.CpsMonad[([_$1] =>> Any)] was found for parameter am of method await in package cps

And looks like this is before macro.

I was able to minimize this to a small program without dptty-cps-async:

trait CpsMonad[F[_]]
def await1[F[_],T](f:F[T])(using am:CpsMonad[F]):T = ???

object MyType
type PseudoMonad[T] =  MyType.type
given myCpsMonad: CpsMonad[PseudoMonad] with { }

object UsedToCompile {
  await1(MyType)
}

Which, I guess, illustrates the dotty bug.

rssh commented

Hmm, maybe not a bug: it can't transform
MyType to PseudoMonad[T], but what this T should be ?.
Any ?, Nothing ?

rcano commented

Hmm, maybe not a bug: it can't transform
MyType to PseudoMonad[T], but what this T should be ?.
Any ?, Nothing ?

I'd say anything the compiler want's to infer, which I bet is Nothing.
Neverhtless I think I found the source of my bug here, the method requests an implicit CpsMonad[F], but it doesn't handle it over to the macro, which then proceeds to do Expr.summon[CpsMonad[F]] to find it.

My problem arises because I'm hiding the cps.async/await calls behind my own inlined calls (that make sense with my DSL), and even though I explicitly typed and pass the implicits manually, that summon still trips up.

As soon as I'm done with work I'll try to reproduce it

rcano commented

I copied pasted the code from my library onto a clean package and it's not happening 😩. Nevertheless the original code in the original location triggers it everytime. Don't know what to do here, in any case, please take a look at the implicit CpsMonad not being forwarded to the macro because maybe that is doable and fixes it for me.

Sorry for the noise.

rssh commented

Yes, minimization can be difficult. Check - are you use top-level given in other source file ? (this will exclude scala/scala3#11479 )

Yet one changes which come to mind, -- starting with 3.0.0-RC1, evaluation of non-transparent macroses moved from Typer to later phase, and now if you macro include await -- it should be transparent).

rssh commented

btw, submitted test-case about not looking into alias to dotty scala/scala3#11607

rssh commented

And close one, because the compiler error is correct: we pass an instance of MyType, the compiler can't deduce PseudoMonad[X]. from MyType, because it's see MyType as an argument, not PseudoMonad.

rssh commented

looks dormant, so can be closed. Free to reopen if find a way to reproduce with the newest version.