GithubHelp home page GithubHelp logo

scala / scala-async Goto Github PK

View Code? Open in Web Editor NEW
1.1K 54.0 92.0 1.76 MB

An asynchronous programming facility for Scala

License: Apache License 2.0

Scala 100.00%
scala scala-async asynchronous concurrency

scala-async's Introduction

scala-async

A Scala DSL to enable a direct style of coding when composing Futures.

Usage

As of scala-async 1.0, Scala 2.12.12+ or 2.13.3+ are required.

Add dependency

SBT Example

libraryDependencies += "org.scala-lang.modules" %% "scala-async" % "1.0.1"
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided

For Maven projects add the following to your (make sure to use the correct Scala version suffix to match your project’s Scala binary version):

Maven Example

<dependency>
  <groupId>org.scala-lang.modules</groupId>
  <artifactId>scala-async_2.13</artifactId>
  <version>1.0.1</version>
</dependency>
<dependency>
  <groupId>org.scala-lang</groupId>
  <artifactId>scala-reflect</artifactId>
  <version>2.13.8</version>
  <scope>provided</scope>
</dependency>

Enable compiler support for async

Add the -Xasync to the Scala compiler options.

SBT Example

scalacOptions += "-Xasync"

Maven Example

<project>
  ...
  <plugin>
    <groupId>net.alchim31.maven</groupId>
    <artifactId>scala-maven-plugin</artifactId>
    <version>4.4.0</version>
    <configuration>
      <args>
        <arg>-Xasync</arg>
      </args>
    </configuration>
  </plugin>
  ...
</project>

Start coding

import scala.concurrent.ExecutionContext.Implicits.global
import scala.async.Async.{async, await}

val future = async {
  val f1: Future[Boolean] = async { ...; true }
  val f2 = async { ...; 42 }
  if (await(f1)) await(f2) else 0
}

What is async?

async marks a block of asynchronous code. Such a block usually contains one or more await calls, which marks a point at which the computation will be suspended until the awaited Future is complete.

By default, async blocks operate on scala.concurrent.{Future, Promise}. The system can be adapted to alternative implementations of the Future pattern.

Consider the following example:

def slowCalcFuture: Future[Int] = ...             // 01
def combined: Future[Int] = async {               // 02
  await(slowCalcFuture) + await(slowCalcFuture)   // 03
}
val x: Int = Await.result(combined, 10.seconds)   // 05

Line 1 defines an asynchronous method: it returns a Future.

Line 2 begins an async block. During compilation, the contents of this block will be analyzed to identify the await calls, and transformed into non-blocking code.

Control flow will immediately pass to line 5, as the computation in the async block is not executed on the caller's thread.

Line 3 begins by triggering slowCalcFuture, and then suspending until it has been calculated. Only after it has finished, we trigger it again, and suspend again. Finally, we add the results and complete combined, which in turn will release line 5 (unless it had already timed out).

It is important to note that while lines 1-4 are non-blocking, they are not parallel. If we wanted to parallelize the two computations, we could rearrange the code as follows:

def combined: Future[Int] = async {
  val future1 = slowCalcFuture
  val future2 = slowCalcFuture
  await(future1) + await(future2)
}

Limitations

await must be directly in the control flow of the async expression

The await cannot be nested under a local method, object, class or lambda:

async {
  List(1).foreach { x => await(f(x) } // invalid
}

await must be not be nested within try / catch / finally.

This implementation restriction may be lifted in future versions.

Comparison with direct use of Future API

This computation could also be expressed by directly using the higher-order functions of Futures:

def slowCalcFuture: Future[Int] = ...
val future1 = slowCalcFuture
val future2 = slowCalcFuture
def combined: Future[Int] = for {
  r1 <- future1
  r2 <- future2
} yield r1 + r2

The async approach has two advantages over the use of map and flatMap:

  1. The code more directly reflects the programmer's intent, and does not require us to name the results r1 and r2. This advantage is even more pronounced when we mix control structures in async blocks.
  2. async blocks are compiled to a single anonymous class, as opposed to a separate anonymous class for each closure required at each generator (<-) in the for-comprehension. This reduces the size of generated code, and can avoid boxing of intermediate results.

scala-async's People

Contributors

adriaanm avatar akkomar avatar alexarchambault avatar atry avatar backuitist avatar dn-mib avatar gnovark avatar jochenschneider avatar jonasackermann avatar jyotman avatar lrytz avatar mpociecha avatar nthportal avatar ondrejspanel avatar phaller avatar philippus avatar raboof avatar retronym avatar rorygraves avatar satendrakumar avatar scala-steward avatar sethtisue avatar som-snytt avatar theon avatar viktorklang avatar xeno-by avatar xuwei-k avatar yadavan88 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

scala-async's Issues

Name of the await function

Why did you choose the name await when it does not await a future?

When working with futures one of the first things you learn is to never use Await.result to prevent blocking. I was (before I understood what the async library was doing) very reluctant to use the await function from Scala async. I still have problems using it because other people in my company sometimes mistake it for an Await.result call.

It might be interesting to use another name. Maybe something like resultOf or valueOf.

Return statement

It would be great to add return statement to async-await library because it simplifies the code.

Consider example with return and without return.

Login function with return statement:
def login(login: String, pass: String): Future[Status] = async {
    val passHashFuture = getPassFromDb(login)
    val passHashOption = await(passHashFuture)

    if (passHashOption.isEmpty) ret(false)

    val pass = passHashOption.get
    val passValid = validatePassword(pass, passHash)

    if (!passValid) ret(false)

    true
}
Login function without return statement:
def login(login: String, pass: String): Future[Status] = async {
    val passHashFuture = getPassFromDb(login)
    val passHashOption = await(passHashFuture)

    if (passHashOption.isDefined) {
        val pass = passHashOption.get
        val passValid = validatePassword(pass, passHash)

        if (passValid) {
            true
        } else {
            false
        }
    } else {
        false
    }
}

As you can see the first case looks much better then second one.

Allow using async in for loops over collections

Hi,

I think in case of (sequential) collections this code

for (x  xs) {
  ... async ...
}

could be rewritten into

val it = xs.iterator
while (it.hasNext) {
  val x = it.next()
  ... async ...
}

thus relaxing the restriction regarding async inside closures. It only saves a few lines, but looks more idiomatic. What do you think?

NPE when a value class is assigned to a val inside async block

It appears that if you use a value class inside an async block, the async macro will throw a NullPointerException while it's trying to initialize the state machine. When I run this code:

class IntWrapper(val value: String) extends AnyVal

async {
  val uid = new IntWrapper("foo")
  await(Future(uid))
}

I get this error:

java.lang.NullPointerException
    at #worksheet#.stateMachine$macro$1$1.<init>(TestCase.sc1225847898409493360.tmp:11)
    at #worksheet#.#worksheet#(TestCase.sc1225847898409493360.tmp:9)

Where is 0.9.3-SNAPSHOT published?

I want to start using a recent snapshot that includes the fixes for #73 and #93, but I can't find published snapshots anywhere. Are they in some repository? If not, do you intend to publish one?

Should var state by synthetic ?

I'm having trouble identifying vars which are generated by the macro. It seems the <synthetic> <stable> private[this] var await$macro$3$macro$7 ints are tagged as synthetic but the private[this] var state: Int is not.

Spurious warning when used in unit function

On Scala 2.11, async 0.9.1, this code works fine but produces a compiler warning:

a pure expression does nothing in statement position; you may be omitting necessary parentheses
await(Future{println("hello")})
     ^
import scala.async.Async._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Random

object AsyncTest {

  def randBool = (new Random).nextBoolean()

  def doStuffWithSideEffect(): String = {
    println("goodbye")
    "foo"
  }

  def test() {
    async {
      if (randBool) {
        await(Future{println("hello")})
        doStuffWithSideEffect()
      }
    }
  }

}

Unless I'm mistaken, this warning shouldn't be there - that is, it doesn't appear if you write a synchronous function with a similar pattern.

Complete / Fix multi-parameter application support

Currently, we have a check in the ANF transform that prevents await usage multi-parameter list methods. This isn't a fundamental limitation, we just haven't written the transform that deals with Apply(Apply(....)) holistically, and doesn't change evaluation order of the arguments.

But, the check is over-eager, and currently stops use of awaitas the receiver for such a method.

Wrong link in README

The last line of README.md says:

See #13 for why await is not possible in closures

But #13 is clearly not the right link target; I don't know what is.

Mismatched existential skolem type error

import scala.async.internal.AsyncId._
class F[A]
class S[A]
def result[A](f: F[A]): S[A with String] = ???
val f: F[_] = ???
val s /* F[$1 with String] */ = result(f)
async {
  val res = await(s)
  if(true) {
    await(0)
  }
  res
}

More test cases and analysis in #77

throw inside async block causes dead code warning

scala> async { throw new Exception }
<console>:13: warning: dead code following this construct

This block does return a failed Future as expected, but the warning is a cosmetic problem. Is it possible to remove? Or is there a better way to fail an async block?

NullPointerException when using a nested await() with an AnyVal class

Running this code:

import scala.concurrent.{Await, Future}
import scala.concurrent.duration.Duration.Inf
import scala.concurrent.ExecutionContext.Implicits.global
import scala.async.Async.{async, await}


object Main {
  def main(args: Array[String]): Unit = {
    val f = async {
      val a = Future.successful(new A(BigInt(42)))
      await(await(a).g)
    }
    Await.result(f, Inf)
  }
}

class A(val b: BigInt) extends AnyVal {
  def g = Future.successful(b + 1)
}

results in an NPE:

java.lang.NullPointerException
    at Main$stateMachine$macro$1$1.resume(Main.scala:9)
    at Main$stateMachine$macro$1$1.apply(Main.scala:9)
    at Main$stateMachine$macro$1$1.apply(Main.scala:9)
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
    at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.pollAndExecAll(ForkJoinPool.java:1253)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1346)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

If extends AnyVal is removed from A, it runs successfully. It also works if the enclosed type is an Int instead of a BigInt.

Scala version: 2.11.2
scala-async version: 0.9.2

do/while crasher

import scala.concurrent._, ExecutionContext.Implicits.global
    def Foo(f: () => Boolean, g: () => Future[Boolean]): Future[Boolean] = async {
      var b = false
      do {
        b = await {
          g()
        }
      } while (b && !f())
      b
    }
scala: Unknown label target: method doWhile$1 at: source-/Users/jason/code/scala-async/src/test/scala/scala/async/run/t48.scala,line-37,offset=947: ctx: package run { class scala.async.run.t48$stateMachine$1$1 { def scala.async.run.t48$stateMachine$1$1.resume { bb=15 } } }
scala: uncaught exception during compilation: scala.reflect.internal.FatalError

Stack overflow in while loop inside `async`

This code causes a stack overflow:

async {
  while (true) {
    if (false) {
      await(Future.successful(()))
    }
  }
}

The generated resume method takes up a stack frame for each iteration of the loop.

Tested with 0.9.2 for scala 2.10. Has this been fixed in the master branch? I don't have time to check at the moment.

Possible regression caused by PR #114 (original issue #74)

I think I’ve come across a regression that I think comes from PR #114.

The reason I think so is that while the issue is observed after upgrading from 0.9.3 to 0.9.4, the issue is not present when compiling against a local build from 5aa390e, the parent of your merge of #114, and #114 was the last thing to go into the 0.9.4 release.

I’ve had trouble constructing a good report of this issue, but hopefully you’ve got a better intuition for what might be at fault here.

The upgrade led to compilation errors such as:

[error] java.lang.AssertionError: assertion failed: TypeApply tpe is null:
[error] stateMachine$macro$10.this.await$macro$16$macro$23.asInstanceOf[Unit]

The full stack trace was:

java.lang.AssertionError: assertion failed: TypeApply tpe is null:
stateMachine$macro$10.this.await$macro$16$macro$23.asInstanceOf[Unit]
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:539)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.internal.Trees$$anonfun$itransform$1.apply(Trees.scala:1357)
    at scala.reflect.internal.Trees$$anonfun$itransform$1.apply(Trees.scala:1356)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:17)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1355)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:477)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:515)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformCaseDefs$1.apply(Trees.scala:2581)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformCaseDefs$1.apply(Trees.scala:2581)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformCaseDefs(Trees.scala:2581)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1382)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1390)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:511)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1421)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1363)
    at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1361)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:17)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1360)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:451)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1404)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.scala$tools$nsc$transform$TypingTransformers$TypingTransformer$$super$transform(TypingTransformers.scala:40)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer$$anonfun$transform$1.apply(TypingTransformers.scala:40)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer$$anonfun$transform$1.apply(TypingTransformers.scala:40)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:40)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.scala$tools$nsc$transform$UnCurry$UnCurryTransformer$$super$transform(UnCurry.scala:469)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:525)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
    at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1408)
    at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1407)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:17)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1406)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1363)
    at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1361)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:17)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1360)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:451)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1404)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.scala$tools$nsc$transform$TypingTransformers$TypingTransformer$$super$transform(TypingTransformers.scala:40)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer$$anonfun$transform$1.apply(TypingTransformers.scala:40)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer$$anonfun$transform$1.apply(TypingTransformers.scala:40)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:40)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.scala$tools$nsc$transform$UnCurry$UnCurryTransformer$$super$transform(UnCurry.scala:469)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:525)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
    at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1408)
    at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1407)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:17)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1406)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
    at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
    at scala.collection.immutable.List.loop$1(List.scala:173)
    at scala.collection.immutable.List.mapConserve(List.scala:189)
    at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
    at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1426)
    at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1426)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:17)
    at scala.reflect.internal.Trees$class.itransform(Trees.scala:1425)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
    at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.scala$tools$nsc$transform$TypingTransformers$TypingTransformer$$super$transform(TypingTransformers.scala:40)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer$$anonfun$transform$2.apply(TypingTransformers.scala:42)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer$$anonfun$transform$2.apply(TypingTransformers.scala:42)
    at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
    at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:42)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.mainTransform(UnCurry.scala:528)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:102)
    at scala.tools.nsc.transform.UnCurry$UnCurryTransformer.transform(UnCurry.scala:66)
    at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:147)
    at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$applyPhase$1.apply$mcV$sp(Global.scala:440)
    at scala.tools.nsc.Global$GlobalPhase.withCurrentUnit(Global.scala:431)
    at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:440)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:398)
    at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:398)
    at scala.collection.Iterator$class.foreach(Iterator.scala:742)
    at scala.collection.AbstractIterator.foreach(Iterator.scala:1194)
    at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:398)
    at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1501)
    at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1486)
    at scala.tools.nsc.Global$Run.compileSources(Global.scala:1481)
    at scala.tools.nsc.Global$Run.compile(Global.scala:1582)
    at xsbt.CachedCompiler0.run(CompilerInterface.scala:116)
    at xsbt.CachedCompiler0.run(CompilerInterface.scala:95)
    at xsbt.CompilerInterface.run(CompilerInterface.scala:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
        at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:101)
    at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:47)
    at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41)
    at sbt.compiler.MixedAnalyzingCompiler$$anonfun$compileScala$1$1.apply$mcV$sp(MixedAnalyzingCompiler.scala:51)
    at sbt.compiler.MixedAnalyzingCompiler$$anonfun$compileScala$1$1.apply(MixedAnalyzingCompiler.scala:51)
    at sbt.compiler.MixedAnalyzingCompiler$$anonfun$compileScala$1$1.apply(MixedAnalyzingCompiler.scala:51)
    at sbt.compiler.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:75)
    at sbt.compiler.MixedAnalyzingCompiler.compileScala$1(MixedAnalyzingCompiler.scala:50)
    at sbt.compiler.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:65)
    at sbt.compiler.IC$$anonfun$compileInternal$1.apply(IncrementalCompiler.scala:160)
    at sbt.compiler.IC$$anonfun$compileInternal$1.apply(IncrementalCompiler.scala:160)
    at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:66)
    at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:64)
    at sbt.inc.IncrementalCommon.cycle(IncrementalCommon.scala:31)
    at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:62)
    at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:61)
    at sbt.inc.Incremental$.manageClassfiles(Incremental.scala:89)
    at sbt.inc.Incremental$.compile(Incremental.scala:61)
    at sbt.inc.IncrementalCompile$.apply(Compile.scala:54)
    at sbt.compiler.IC$.compileInternal(IncrementalCompiler.scala:160)
    at sbt.compiler.IC$.incrementalCompile(IncrementalCompiler.scala:138)
    at sbt.Compiler$.compile(Compiler.scala:128)
    at sbt.Compiler$.compile(Compiler.scala:114)
    at sbt.Defaults$.sbt$Defaults$$compileIncrementalTaskImpl(Defaults.scala:814)
    at sbt.Defaults$$anonfun$compileIncrementalTask$1.apply(Defaults.scala:805)
    at sbt.Defaults$$anonfun$compileIncrementalTask$1.apply(Defaults.scala:803)
    at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
    at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
    at sbt.std.Transform$$anon$4.work(System.scala:63)
    at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
    at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
    at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
    at sbt.Execute.work(Execute.scala:235)
    at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
    at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
    at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
    at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

While investigating the issue I managed to come across a different error report:

[error] java.lang.AssertionError: assertion failed: 
[error]   TypeApply tpe is null:
[error] stateMachine$macro$1.this.await$macro$9$macro$19.asInstanceOf[Unit]
[error]      while compiling: (…snip…)
[error]         during phase: uncurry
[error]      library version: version 2.11.7
[error]     compiler version: version 2.11.7
[error]   reconstructed args: -Xfuture -deprecation -feature -Xfatal-warnings -classpath (…snip…)

The end of that report contains production code that I can’t post here, but I’d be happy to send it privately.

Re-ordering statements for more optimal parallel future execution

It is a well known issue with for-comprehensions where the futures in the code block below are not executed as parallel as possible:

for {
  val1 <- getSomeFuture1()
  val2 <- getSomeFuture2()
} yield ...

We can manually solve this by rewriting the code as the code block:

val future1 = getSomeFuture1()
val future2 = getSomeFuture2()
for {
  val1 <- future1
  val2 <- future2
} yield ...

This transformation can be done only when the following two criteria are met:

  1. The creation of future2 doesn't depend on the result of future1; and,
  2. The evaluation of the value of future1 doesn't influence the evaluation of getSomeFuture2 and future2.

This manual transformation is quite tedious. This transformation is pretty mechanical and therefore ideally suited for a macro or compiler optimisation. Also, we need to a way to verify the two conditions hold, unless we leave that to the developer.

The reason why I suggest performing the transformation on the level of futures instead of await/async is so it can be used in more scenarios.

interaction with dependent / singleton types

import scala.async.AsyncId.{async, await}
class A { class B }
async {
  val a = new A
  def foo(b: a.B) = 0
  await(foo(new a.B))
}

➡️

 found   : a.B
 required: a.B
                await(foo(new a.B))
                          ^

similar:

async {
  val x = ""
  def foo(a: x.type) = 0
  await(foo(x))
}

➡️

 found   : x.type (with underlying type String)
 required: x.type
                await(foo(x))
                          ^

and also (foo and await exchanged):

async {
  val x = ""
  def foo(a: x.type) = 0
  foo(await(x))
}

➡️

 found   : String
 required: x.type
                foo(await(x))
                         ^

Awaitable operations inside try block

Why not support awaitable operations inside try block, by translating
try{
F
} catch {
G
} finally {
H
}
to something like: async(F).recoverWith(G).onComplete(H) ?

Include dependency information in README

There's no mention of the Maven coordinates for including scala-async, nor the latest version. Some people might get confused (I tried org.scala-lang and finally I had to go to search.maven.org to find the proper group ID).

await as an extension method

Can we have await as an extension method, a la apply in Akka Dataflow? This could make some code a bit nicer to read and write.

NPE during compilation

During compilation of production code using scala-async 0.9.3 and scala 2.11.7 I've got an exception:

java.lang.NullPointerException
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1695)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1386)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1250)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.collection.immutable.List.flatMap(List.scala:327)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1742)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.api.Trees$Transformer$$anonfun$transformCaseDefs$1.apply(Trees.scala:2581)
        at scala.reflect.api.Trees$Transformer$$anonfun$transformCaseDefs$1.apply(Trees.scala:2581)
        at scala.collection.immutable.List.loop$1(List.scala:173)
        at scala.collection.immutable.List.mapConserve(List.scala:189)
        at scala.reflect.api.Trees$Transformer.transformCaseDefs(Trees.scala:2581)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1382)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1390)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1250)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.collection.immutable.List.flatMap(List.scala:327)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1250)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.collection.immutable.List.flatMap(List.scala:327)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1421)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1363)
        at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1361)
        at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1360)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1250)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.collection.immutable.List.flatMap(List.scala:327)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1404)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
        at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1408)
        at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1407)
        at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1406)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1250)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.collection.immutable.List.flatMap(List.scala:327)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1366)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1363)
        at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1361)
        at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1360)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1250)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.collection.immutable.List.flatMap(List.scala:327)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:111)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1404)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
        at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1408)
        at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1407)
        at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1406)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStat(RefChecks.scala:1250)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$transformStats$1.apply(RefChecks.scala:1165)
        at scala.collection.immutable.List.flatMap(List.scala:327)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:1165)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformStats(RefChecks.scala:111)
        at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1426)
        at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1426)
        at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
        at scala.reflect.internal.Trees$class.itransform(Trees.scala:1425)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
        at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:1764)
        at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transform(RefChecks.scala:111)
        at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:147)
        at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
        at scala.tools.nsc.Global$GlobalPhase$$anonfun$applyPhase$1.apply$mcV$sp(Global.scala:440)
        at scala.tools.nsc.Global$GlobalPhase.withCurrentUnit(Global.scala:431)
        at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:440)
        at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:398)
        at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:398)
        at scala.collection.Iterator$class.foreach(Iterator.scala:742)
        at scala.collection.AbstractIterator.foreach(Iterator.scala:1194)
        at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:398)
        at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1501)
        at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1486)
        at scala.tools.nsc.Global$Run.compileSources(Global.scala:1481)
        at scala.tools.nsc.Global$Run.compile(Global.scala:1582)
        at xsbt.CachedCompiler0.run(CompilerInterface.scala:116)
        at xsbt.CachedCompiler0.run(CompilerInterface.scala:95)
        at xsbt.CompilerInterface.run(CompilerInterface.scala:26)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at sbt.compiler.AnalyzingCompiler.call(AnalyzingCompiler.scala:101)
        at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:47)
        at sbt.compiler.AnalyzingCompiler.compile(AnalyzingCompiler.scala:41)
        at sbt.compiler.MixedAnalyzingCompiler$$anonfun$compileScala$1$1.apply$mcV$sp(MixedAnalyzingCompiler.scala:51)
        at sbt.compiler.MixedAnalyzingCompiler$$anonfun$compileScala$1$1.apply(MixedAnalyzingCompiler.scala:51)
        at sbt.compiler.MixedAnalyzingCompiler$$anonfun$compileScala$1$1.apply(MixedAnalyzingCompiler.scala:51)
        at sbt.compiler.MixedAnalyzingCompiler.timed(MixedAnalyzingCompiler.scala:75)
        at sbt.compiler.MixedAnalyzingCompiler.compileScala$1(MixedAnalyzingCompiler.scala:50)
        at sbt.compiler.MixedAnalyzingCompiler.compile(MixedAnalyzingCompiler.scala:65)
        at sbt.compiler.IC$$anonfun$compileInternal$1.apply(IncrementalCompiler.scala:160)
        at sbt.compiler.IC$$anonfun$compileInternal$1.apply(IncrementalCompiler.scala:160)
        at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:66)
        at sbt.inc.IncrementalCompile$$anonfun$doCompile$1.apply(Compile.scala:64)
        at sbt.inc.IncrementalCommon.cycle(IncrementalCommon.scala:31)
        at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:62)
        at sbt.inc.Incremental$$anonfun$1.apply(Incremental.scala:61)
        at sbt.inc.Incremental$.manageClassfiles(Incremental.scala:89)
        at sbt.inc.Incremental$.compile(Incremental.scala:61)
        at sbt.inc.IncrementalCompile$.apply(Compile.scala:54)
        at sbt.compiler.IC$.compileInternal(IncrementalCompiler.scala:160)
        at sbt.compiler.IC$.incrementalCompile(IncrementalCompiler.scala:138)
        at sbt.Compiler$.compile(Compiler.scala:128)
        at sbt.Compiler$.compile(Compiler.scala:114)
        at sbt.Defaults$.sbt$Defaults$$compileIncrementalTaskImpl(Defaults.scala:814)
        at sbt.Defaults$$anonfun$compileIncrementalTask$1.apply(Defaults.scala:805)
        at sbt.Defaults$$anonfun$compileIncrementalTask$1.apply(Defaults.scala:803)
        at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
        at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
        at sbt.std.Transform$$anon$4.work(System.scala:63)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
        at sbt.Execute.work(Execute.scala:235)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
        at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

cc @retronym

“type [...] is not a class” on 2.11.0-RC1

Hi,

I have roughly the following things:

class Ui[+A](protected val v: ()  A) {
  /** Run the code on the UI thread */
  def run: Future[A] = ...
  /** Get the result of executing the code on the current thread */
  def get: A = ...
}

trait CanSnail[W, S, R] {
  def snail(w: W, s: S): Ui[Future[R]]
}

implicit def `List is snailable`[W, S, R](implicit canSnail: CanSnail[W, S, R]) =
  new CanSnail[List[W], S, List[W]] {
    def snail(l: List[W], s: S) = Ui(async {
      val it = l.iterator
      while (it.hasNext) {
        // we can call Ui.get, since we are already inside the UI thread
        await(canSnail.snail(it.next(), s).get)
      }
      l
    }(UiThreadExecutionContext))
  }

On Scala 2.11.0-RC1 (but not 2.10.3) it fails with

scala.ScalaReflectionException: type R is not a class
[error]     at scala.reflect.api.Symbols$SymbolApi$class.asClass(Symbols.scala:272)
[error]     at scala.reflect.internal.Symbols$SymbolContextApiImpl.asClass(Symbols.scala:82)
[error]     at scala.async.internal.LiveVariables$$anonfun$3.apply(LiveVariables.scala:58)
[error]     at scala.async.internal.LiveVariables$$anonfun$3.apply(LiveVariables.scala:57)
[error]     at scala.collection.TraversableLike$$anonfun$filterImpl$1.apply(TraversableLike.scala:259)
[error]     at scala.collection.immutable.Set$Set2.foreach(Set.scala:113)
[error]     at scala.collection.TraversableLike$class.filterImpl(TraversableLike.scala:258)
[error]     at scala.collection.TraversableLike$class.filter(TraversableLike.scala:270)
[error]     at scala.collection.AbstractTraversable.filter(Traversable.scala:104)
[error]     at scala.async.internal.LiveVariables$class.liveVars(LiveVariables.scala:57)
[error]     at scala.async.internal.AsyncMacro$$anon$1.liveVars(AsyncMacro.scala:6)
[error]     at scala.async.internal.LiveVariables$class.fieldsToNullOut(LiveVariables.scala:20)
[error]     at scala.async.internal.AsyncMacro$$anon$1.fieldsToNullOut(AsyncMacro.scala:6)
[error]     at scala.async.internal.AsyncTransform$class.asyncTransform(AsyncTransform.scala:76)
[error]     at scala.async.internal.AsyncMacro$$anon$1.asyncTransform(AsyncMacro.scala:6)
[error]     at scala.async.internal.AsyncBase.asyncImpl(AsyncBase.scala:48)
[error]     at scala.async.internal.ScalaConcurrentAsync$.asyncImpl(ScalaConcurrentAsync.scala:16)
[error]       }(UiThreadExecutionContext))
[error]        ^

I’m using async milestone M5 on both. Perhaps a regression somewhere?
I’ll be able to push the complete code tomorrow, if that’s necessary.

Nick

scala.NotImplementedError when evaluating a failed Future

The following worksheet:

import scala.async.Async.{async, await}
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

val f = Future.failed(new Exception())
async {
  await(f)
}

delivers the following error

scala.NotImplementedError: an implementation is missing
    at scala.Predef$.$qmark$qmark$qmark(async.sc1820668219168430883.tmp:248)
    at #worksheet#.stateMachine$1$1.<init>(async.sc1820668219168430883.tmp:7)
    at #worksheet#.#worksheet#(async.sc1820668219168430883.tmp:6)

Better positions in trees

Positions are not being set correctly on the trees inside an async block. This manifests itself in the IDE in a way where everything points to the enclosing def.

Proguard errors on 0.9.5

I'm attempting to use the Scala 2.11 version of 0.9.5 (obtained through SBT) on Android. After upgrading from 0.9.4 I get the following proguard errors.

Warning: scala.async.internal.AsyncTransform$$typecreator3$1: can't find enclosing method 'scala.reflect.api.Trees$TreeApi startStateMachine$1(scala.async.internal.AsyncMacro,scala.reflect.api.TypeTags$WeakTypeTag,scala.reflect.api.Trees$ClassDefApi,scala.async.internal.ExprBuilder$AsyncBlock,scala.collection.immutable.List)' in program class scala.async.internal.AsyncTransform
Warning: scala.async.internal.LiveVariables$FindUseTraverser$1: can't find enclosing method 'scala.async.internal.LiveVariables$ReferencedFields$3 fieldsUsedIn$1(scala.async.internal.AsyncMacro,scala.async.internal.ExprBuilder$AsyncState,scala.collection.immutable.Set,scala.runtime.VolatileObjectRef)' in program class scala.async.internal.LiveVariables

I couldn't find anything called typecreator in the source code and fieldsUsedIn appears to have a different signature.

After reverting back to 0.9.4 I do not get these errors.

How to include via sbt

Hi, is async available from maven or some such so that I can't include it via sbt easily?

I couldn't find it on maven from a cursory glance and the README just says include the jar which I can do. Just I was hoping I wouldn't have to do this by hand.

Thanks.

Document how error handling works

How are failed futures handled? Does the whole async block just fail? Is there a way to handle individual failed awaits within one async block, or is the only solution there to nest async blocks? If that's the case, perhaps a tryAwait that returns a Try would be helpful. In any case, a couple examples in the docs would be great. Thanks

await() on already-completed future could return value synchronously

await(future) always compiles to future.onComplete(...), even if the future is already completed. (I think so based on the output of scalac --Ymacro-debug-lite in the 2.10 branch.) Why not return the value synchronously if it is already available?

Here's a simple implementation as a macro wrapping await():

  def fastAwait[T](fut: Future[T]): T = macro fastAwaitImpl[T]

  def fastAwaitImpl[T: c.WeakTypeTag](c: Context)(fut: c.Expr[Future[T]]): c.Expr[T] = {
    import c.universe._
    val tree =  q"{ val f = $fut; if (f.isCompleted) f.value.get.get else await(f) }"
    c.Expr[T](tree)
  }

I have code that passes very large amounts of short-lived Futures around. Many of these are already completed when awaited. This optimization helps (from initial tests) and I'm considering using it in my code - is there any reason not to do this?

Chain exception causes

When an asynchronous operation that is a part of another asynchronous operation fails, it would be very helpful to get a full stacktrace from the topmost async to the bottommost Future that reported the failure. Currently, only the bottommost failure is reported. This + lack of ability to debug + lack of try/catch makes async/await code very hard to debug.

Example:

def doSomethingAsync: Future[Int] = {
   async {
   // ... 
     throw new IOException("Something went wrong...")  // this line is reported properly
   }
}

def caller: Future[Int] = {
  async {
     await(doSomethingAsync)    // << I'd like this line to be reported in the stacktrace as well
  }
}

NotImplementedError is being thrown in...

... this example

scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.async.Async._
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global

def broken() = async {
  if (true) {
    val n = await(async(1))
    if (n < 2) {
      throw new RuntimeException("case a")
    }
    else {
      throw new RuntimeException("case b")
    }
  }
  else {
    "case c"
  }
}

// Exiting paste mode, now interpreting.

import scala.async.Async._
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global
broken: ()scala.concurrent.Future[String]

scala> println(Await.result(broken(), Duration("1 second")))
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at stateMachine$macro$2$1.apply(<console>:15)
  at stateMachine$macro$2$1.apply(<console>:12)
  at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32)
  at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
  at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
  at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
  at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
  at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

Maybe related to #104 and #66?

A workaround for this issue is to provide an explicit return type of Future[String] to the method.

await on a Future[Nothing] can cause NotImplementedError

I have some code that makes an async call do decide which exception to throw. This seems to generate an assignment to ??? in the calling method when that exception state is skipped.

import scala.async.Async._
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.ExecutionContext.Implicits.global

def errorGenerator(randomNum: Double) = {
  Future {
    if (randomNum < 0) {
      throw new IllegalStateException("Random number was too low!")
    } else {
      throw new IllegalStateException("Random number was too high!")
    }
  }
}
def randomTimesTwo = async {
  val num = math.random
  if (num < 0 || num > 1) {
    await(errorGenerator(num))
  }

  num * 2
}

println(Await.result(randomTimesTwo, Duration("1 second")))

results in

java.util.concurrent.ExecutionException: Boxed Error
    at scala.concurrent.impl.Promise$.resolver(Promise.scala:55)
    at scala.concurrent.impl.Promise$.scala$concurrent$impl$Promise$$resolveTry(Promise.scala:47)
    at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:244)
    at scala.concurrent.Promise$class.complete(Promise.scala:55)
    at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:153)
    at A$A110$A$A110$stateMachine$macro$1$1.resume(playground.sc0.tmp:20)
    at A$A110$A$A110$stateMachine$macro$1$1.resume(playground.sc0.tmp:20)
    at A$A110$A$A110$stateMachine$macro$1$1.apply$mcV$sp(playground.sc0.tmp:20)
    at A$A110$A$A110$stateMachine$macro$1$1.apply(playground.sc0.tmp:20)
    at A$A110$A$A110$stateMachine$macro$1$1.apply(playground.sc0.tmp:20)
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
    at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
Caused by: scala.NotImplementedError: an implementation is missing
    at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
    ... 12 more

Tried this on async 0.9.2 & 0.9.3, Scala 2.11.4.

crash with nested class

  @Test
  def crsh() {
    import scala.async.AsyncId.{async, await}
    async {
      class B { def f = 1 }
      await(new B()).f
    } mustBe 1
  }

➡️

[error] Test scala.async.run.anf.AnfTransformSpec.crsh failed: scala/async/run/anf/AnfTransformSpec/crsh$B
[error]     at scala.async.run.anf.AnfTransformSpec$stateMachine$114$1.apply(AnfTransformSpec.scala:358)
[error]     at scala.async.run.anf.AnfTransformSpec$stateMachine$114$1.resume$async(AnfTransformSpec.scala:358)
[error]     at scala.async.run.anf.AnfTransformSpec$stateMachine$114$1.apply$mcV$sp(AnfTransformSpec.scala:358)
[error]     at scala.async.run.anf.AnfTransformSpec.crsh(AnfTransformSpec.scala:358)
[error]     ...
[error] Caused by: java.lang.ClassNotFoundException: scala.async.run.anf.AnfTransformSpec.crsh$B
[error]     at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
[error]     at java.security.AccessController.doPrivileged(Native Method)
[error]     at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
[error]     at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
[error]     at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
[error]     ... 70 more

FutureSystem impl where futureSystem.Tryy != scala.util.Try fails to compile

I have an implementation of FutureSystem nearly complete to connect scala-async with the Twitter util-core implementation of futures. It fails to compile with this error:

[error] /Users/tdyas/Code/twitter-util-async/src/test/scala/com/foursquare/common/async/TwitterFutureSystemTest.scala:16: class stateMachine$1 needs to be abstract, since method apply in trait Function1 of type (v1: scala.util.Try[Any])Unit is not defined
[error] (Note that T1 does not match com.twitter.util.Try[Any])
[error]     val f3 = async {

The issue seems to be the hard-coded reference to scala.util.Try in src/main/scala/scala/async/internal/AsyncTransform.scala:

val template = Template(List(typeOf[(scala.util.Try[Any] => Unit)], typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body)

Basically, the apply method is expecting com.twitter.util.Try but scala-async is hard-coding scala.util.Try.

The AST produced by the code is:

asyncImpl: tree=Expr[AsyncBase.this.futureSystem.Fut[Int]]({
  class stateMachine$1 extends scala.util.Try[Any] => Unit with () => Unit {
    <synthetic> <stable> private[this] var await$2$1: Int = 0;
    <synthetic> <stable> private[this] var await$1$1: Int = 0;
    <stable> private[this] var v1$1: Int = 0;
    def <init>(): stateMachine$1 = {
      stateMachine$1.super.<init>();
      ()
    };
    private[this] var state: Int = 0;
    private[this] val result: com.twitter.util.Promise[Int] = Promise.apply[Int]();
    private[this] val execContext: scala.concurrent.ExecutionContextExecutor = scala.concurrent.ExecutionContext.Implicits.global;
    def resume(): Unit = try {
      stateMachine$1.this.state match {
        case 0 => {
          ();
          <synthetic> val awaitable$1: com.twitter.util.Future[Int] @scala.reflect.internal.annotations.uncheckedBounds = f1;
          {
            awaitable$1.respond(((x: com.twitter.util.Try[Int]) => {
              this.apply(x);
              ()
            }));
            ()
          };
          ()
        }
        case 1 => {
          stateMachine$1.this.v1$1 = stateMachine$1.this.await$1$1;
          <synthetic> val awaitable$2: com.twitter.util.Future[Int] @scala.reflect.internal.annotations.uncheckedBounds = f2;
          {
            awaitable$2.respond(((x: com.twitter.util.Try[Int]) => {
              this.apply(x);
              ()
            }));
            ()
          };
          ()
        }
        case 2 => {
          stateMachine$1.this.result.update(Return.apply[Int]({
            val v2: Int = stateMachine$1.this.await$2$1;
            stateMachine$1.this.v1$1.+(v2)
          }));
          ()
        }
      }
    } catch {
      case (throwable @ _) if NonFatal.apply(throwable) => {
        stateMachine$1.this.result.update(Throw.apply[Int](throwable));
        ()
      }
    };
    def apply(tr: com.twitter.util.Try[Any]): Unit = stateMachine$1.this.state match {
      case 0 => {
        if (tr.isThrow)
          {
            stateMachine$1.this.result.update(tr.asInstanceOf[com.twitter.util.Try[Int]]);
            ()
          }
        else
          {
            stateMachine$1.this.await$1$1 = tr.get().asInstanceOf[Int];
            stateMachine$1.this.state = 1;
            stateMachine$1.this.resume()
          };
        ()
      }
      case 1 => {
        if (tr.isThrow)
          {
            stateMachine$1.this.result.update(tr.asInstanceOf[com.twitter.util.Try[Int]]);
            ()
          }
        else
          {
            stateMachine$1.this.await$2$1 = tr.get().asInstanceOf[Int];
            stateMachine$1.this.state = 2;
            stateMachine$1.this.resume()
          };
        ()
      }
    };
    def apply: Unit = resume();
    private[this] val extra: Unit = ()
  };
  val stateMachine$1 = new stateMachine$1();
  {
    val promise = Promise.apply[Unit]();
    stateMachine$1.execContext.execute({
      final class $anon extends Runnable {
        def <init>() = {
          super.<init>();
          ()
        };
        def run(): Unit = try {
          promise.setValue(stateMachine$1.apply())
        } catch {
          case (ex @ (_: `package`.Exception)) => promise.setException(ex)
        }
      };
      new $anon()
    });
    promise
  };
  stateMachine$1.result
})

I tried changing AsyncTransform.scala to refer to typeOf[futureSystem.Tryy[Any] => Unit] instead but then running the scala-async tests gave errors like this:

[error] /Users/tdyas/Code/scala-async/src/test/scala/scala/async/run/ifelse3/IfElse3.scala:24: type mismatch;
[error]  found   : stateMachine$1
[error]  required: scala.util.Try[Int] => ?
[error]   def m(y: Int): Future[Int] = async {
[error]                                      ^

Any ideas?

"implicit class ... extends AnyVal" with generic methods doesn't work inside async{}

I hit this issue when working through the Reactive Programming week 3 assignment on Coursera, where they use a class with the declaration:

implicit class FutureCompanionOps[T](val f: Future.type) extends AnyVal

Within that class are many methods, one example is:

def always[T](value: T): Future[T] = ???

I haven't included the answer here, because the assignment is still in progress. However, I've boiled the problem I was having down into this simple case:

scala> System.setProperty("scala.async.trace", "true")
res0: String = null

scala> System.setProperty("scala.async.debug", "true")
res1: String = null

scala> import scala.concurrent._
import scala.concurrent._

scala> import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global

scala> import scala.concurrent.duration._
import scala.concurrent.duration._

scala> import scala.async.Async.{async,await}
import scala.async.Async.{async, await}

scala>

scala> implicit class ListTest[T](val l: List.type) extends AnyVal {
     |   def single[S](v: S): List[S] = List(v)
     | }
defined class ListTest

scala> val f = async { List.single(1) }
<console>:17: error: macro has not been expanded
       val f = async { List.single(1) }
               ^

Note that with tracing and debugging enabled, there is no output. I made another async call to ensure they were properly enabled, and they were. I'm not sure if this is a problem with scala-async or in Scala (or with my own usage of these two).

It's easy enough to work around, like:

scala> def s = List.single(1)
s: List[Int]

scala> val f = async { s }
[async] anf({\n  ();\n  $line11.$read.$iw.$iw.$iw.$iw.$iw.$iw.s\n})
[async] = List({\n  ();\n  $line11.$read.$iw.$iw.$iw.$iw.$iw.$iw.s\n})
[async] In file '<console>':
[async] scala.async.Async.async[List[Int]]($line11.$read.$iw.$iw.$iw.$iw.$iw.$iw.s)(scala.concurrent.ExecutionContext.Implicits.global)
[async] ANF transform expands to:
 {
  {
    ();
    $line11.$read.$iw.$iw.$iw.$iw.$iw.$iw.s
  }
}
[async] AsyncState #0, next = 2147483647
[async] fields never zero-ed out:
[async] fields used in state #0:
[async] LVentry at state #0:
[async] LVexit  at state #0:
[async] async state machine transform expands to:
 Future.apply($line11.$read.$iw.$iw.$iw.$iw.$iw.$iw.s)(scala.concurrent.ExecutionContext.Implicits.global)
f: scala.concurrent.Future[List[Int]] = scala.concurrent.impl.Promise$DefaultPromise@7607d6e6

This is a pretty extreme edge case, but I thought I'd share my findings in case they're helpful in improving the library.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.