Pre SIP: `for` with Control Flow (an alternative to Scala Async, Scala Continuations and Scala Virtualized)

I don’t know if it is the case but the following code looks like a retypechecking to me:

You dont’t need to re-typecheck using ReTyper. Dotty can produce well typed trees by construction using just the constructs in tpd and TypeAssigner. I suggest giving them a close look before starting out, since they are quite different from the Scala 2 tools.

I did. It seems that they cannot infer type parameters. Correct me if I was wrong.

I did. It seems that they cannot infer type parameters. Correct me if I was wrong.

You are correct. Type parameters have to be given manually by the translation. But what I would recommend is to run the translation after erasure, when type parameters have been eliminated anyway. This means that the implementation type of an async/await abstraction has to offer an erased API, which might make it less convenient. But that’s OK, I believe, since there are many more uses than implementations so it’s OK to put a higher burden on the implementors.

  • Martin

What cannot be inferred includes not only the type parameters, but also implicit parameters, which cannot be assigned manually after erasure. Another issue is that we cannot create a runtime with inline functions if the translation happens after erasure, excluding the runtime optimization that I mentioned in this post.

Also moving a typed AST to a new closure is dangerous, causing symbol/scope issues, especially when there are class/object definitions in the AST. For example:

def myAsyncFunction[A](myFuture: Future[A]) = async {
  val myValue = await(myFuture)
  object MyObject {
    def myMethod1 = myValue
    def myMethod2 = myFuture
  }
  MyObject
}

It should be translated to:

def myAsyncFunction[A](myFuture: Future[A]) = myFuture.map { myValue =>
  object MyObject {
    def myMethod1 = myValue
    def myMethod2 = myFuture
  }
  MyObject
}

However it will crash due to moving typed AST of definition of MyObject if the translation happens after typer.

I would suggest to implement Async in typer. I understand your concern about increasing the complexity of typer, but the impact should be minimal, since it can be implemented as an optional translation in a modularized trait. After all, if we can expand Dynamic in typer, why not Async?

@odersky, I can create a PR to implement Async in typer if this approach is welcomed.

I think the translation from Async to underlying maond needs to be done after typer, for the reasons of modularity, and probably after erasure, for reasons of compile-speed. We really should take the experience of the 2.13 project into account here. @retronym found that a translation after typer is more efficient in terms of compile-time and more robust in corner cases. That matches my own experience 100%.

Moving typed trees after typer between owners will crash Dotty. What do you think?

Moving typed trees after typer between owners will crash Dotty. What do you think?

You have to do a changeOwner on these trees.