Skip to content

Commit d7b26b3

Browse files
committed
Get rid of the Promise stub.
1 parent 6a24bf2 commit d7b26b3

File tree

4 files changed

+249
-229
lines changed

4 files changed

+249
-229
lines changed
Lines changed: 62 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,150 +1,91 @@
11
package scala.js
22

33
import virtualization.lms.common._
4-
import concurrent.Future
5-
import java.io.PrintWriter
4+
import concurrent.{Promise, Future}
65

7-
trait FutureOps { this: Base =>
6+
trait FutureOps { this: Base with IfThenElse =>
87

9-
trait FutureOpsBase[+A] {
10-
def map[B : Manifest](f: Rep[A] => Rep[B]): Rep[Future[B]]
11-
def flatMap[B : Manifest](f: Rep[A] => Rep[Future[B]]): Rep[Future[B]]
12-
def foreach(f: Rep[A] => Rep[Unit]): Rep[Unit]
13-
def withFilter(f: Rep[A] => Rep[Boolean]): Rep[Future[A]]
8+
def promise[A : Manifest]: Rep[Promise[A]]
9+
implicit class PromiseOps[A : Manifest](p: Rep[Promise[A]]) extends Serializable {
10+
def put(v: Rep[A]) = promise_put(p, v)
11+
def future = promise_future(p)
1412
}
1513

16-
type FutureOpsCls[+A] <: FutureOpsBase[A]
17-
implicit def FutureOpsCls[A : Manifest](f: Rep[Future[A]]): FutureOpsCls[A]
14+
def promise_put[A : Manifest](p: Rep[Promise[A]], v: Rep[A]): Rep[Unit]
15+
def promise_future[A : Manifest](p: Rep[Promise[A]]): Rep[Future[A]]
1816

19-
def future[A : Manifest](a: Rep[A]): Rep[Future[A]] // Wrong: should be a by-name parameter
20-
}
21-
22-
trait FutureOpsExp extends FutureOps with EffectExp {
23-
24-
case class FuturePure[A](a: Exp[A]) extends Def[Future[A]]
25-
case class FutureMap[A, B](a: Exp[Future[A]], x: Sym[A], b: Block[B]) extends Def[Future[B]]
26-
case class FutureFlatMap[A, B](a: Exp[Future[A]], x: Sym[A], b: Block[Future[B]]) extends Def[Future[B]]
27-
case class FutureForeach[A](a: Exp[Future[A]], x: Sym[A], b: Block[Unit]) extends Def[Unit]
28-
case class FutureFilter[A](a: Exp[Future[A]], x: Sym[A], b: Block[Boolean]) extends Def[Future[A]]
2917

30-
def future[A : Manifest](a: Rep[A]) = FuturePure(a)
31-
32-
implicit class FutureOpsCls[+A : Manifest](a: Exp[Future[A]]) extends FutureOpsBase[A] {
33-
def map[B: Manifest](f: Exp[A] => Exp[B]) = {
34-
val x = fresh[A]
35-
val b = reifyEffects(f(x))
36-
reflectEffect(FutureMap(a, x, b), summarizeEffects(b).star)
18+
def future[A : Manifest](a: Rep[A]): Rep[Future[A]]
19+
implicit class FutureOps[A : Manifest](fa: Rep[Future[A]]) extends Serializable {
20+
def foreach(f: Rep[A] => Rep[Unit]): Rep[Unit] = future_foreach(fa, f)
21+
def map[B : Manifest](f: Rep[A] => Rep[B]): Rep[Future[B]] = {
22+
val pb = promise[B]
23+
for (a <- fa) pb.put(f(a))
24+
pb.future
3725
}
38-
39-
def flatMap[B: Manifest](f: Exp[A] => Exp[Future[B]]) = {
40-
val x = fresh[A]
41-
val b = reifyEffects(f(x))
42-
reflectEffect(FutureFlatMap(a, x, b), summarizeEffects(b).star)
26+
def flatMap[B : Manifest](f: Rep[A] => Rep[Future[B]]): Rep[Future[B]] = {
27+
val pb = promise[B]
28+
for {
29+
a <- fa
30+
b <- f(a)
31+
} pb.put(b)
32+
pb.future
4333
}
44-
45-
def foreach(f: Exp[A] => Exp[Unit]) = {
46-
val x = fresh[A]
47-
val b = reifyEffects(f(x))
48-
reflectEffect(FutureForeach(a, x, b), summarizeEffects(b).star)
49-
}
50-
51-
def withFilter(f: Exp[A] => Exp[Boolean]) = {
52-
val x = fresh[A]
53-
val b = reifyEffects(f(x))
54-
reflectEffect(FutureFilter(a, x, b), summarizeEffects(b).star)
34+
def withFilter(f: Rep[A] => Rep[Boolean]): Rep[Future[A]] = {
35+
val pa = promise[A]
36+
for (a <- fa) if (f(a)) pa.put(a)
37+
pa.future
5538
}
5639
}
5740

41+
def future_foreach[A : Manifest](fa: Rep[Future[A]], f: Rep[A] => Rep[Unit]): Rep[Unit]
42+
}
43+
44+
trait FutureOpsExp extends FutureOps with EffectExp { this: IfThenElse with Functions =>
5845

59-
override def syms(e: Any) = e match {
60-
case FutureMap(a, _, b) => syms(a) ++ syms(b)
61-
case FutureFlatMap(a, _, b) => syms(a) ++ syms(b)
62-
case FutureForeach(a, _, b) => syms(a) ++ syms(b)
63-
case FutureFilter(a, _, b) => syms(a) ++ syms(b)
64-
case _ => super.syms(e)
65-
}
46+
case class PromiseNew[A](value: Option[Exp[A]]) extends Def[Promise[A]]
47+
case class PromisePut[A](p: Exp[Promise[A]], v: Exp[A]) extends Def[Unit]
48+
case class PromiseFuture[A](p: Exp[Promise[A]]) extends Def[Future[A]]
49+
case class FutureForeach[A](fa: Exp[Future[A]], f: Exp[A => Unit]) extends Def[Unit]
6650

67-
override def boundSyms(e: Any) = e match {
68-
case FutureMap(_, x, b) => x +: effectSyms(b)
69-
case FutureFlatMap(_, x, b) => x +: effectSyms(b)
70-
case FutureForeach(_, x, b) => x +: effectSyms(b)
71-
case FutureFilter(_, x, b) => x +: effectSyms(b)
72-
case _ => super.boundSyms(e)
73-
}
51+
def promise[A : Manifest] = reflectEffect(PromiseNew[A](None))
52+
def promise_put[A : Manifest](p: Exp[Promise[A]], v: Exp[A]) = reflectEffect(PromisePut(p, v))
53+
def promise_future[A : Manifest](p: Exp[Promise[A]]) = PromiseFuture(p)
54+
55+
def future[A : Manifest](a: Exp[A]) = (PromiseNew(Some(a)): Exp[Promise[A]]).future
56+
def future_foreach[A : Manifest](fa: Exp[Future[A]], f: Exp[A] => Exp[Unit]) = reflectEffect(FutureForeach(fa, f))
7457

75-
override def symsFreq(e: Any) = e match {
76-
case FutureMap(a, _, b) => freqNormal(a) ++ freqNormal(b)
77-
case FutureFlatMap(a, _, b) => freqNormal(a) ++ freqNormal(b)
78-
case FutureForeach(a, _, b) => freqNormal(a) ++ freqNormal(b)
79-
case FutureFilter(a, _, b) => freqNormal(a) ++ freqNormal(b)
80-
case _ => super.symsFreq(e)
81-
}
8258
}
8359

8460
trait JSGenFutureOps extends JSNestedCodegen {
8561
val IR: FutureOpsExp
8662
import IR._
8763

8864
override def emitNode(sym: Sym[Any], rhs: Def[Any]) = rhs match {
89-
case FuturePure(a) =>
90-
emitValDef(sym, "new Promise(" + quote(a) + ")")
91-
case FutureMap(a, x, b) =>
92-
emitValDef(sym, "new Promise()")
93-
stream.println(quote(a) + ".onComplete(function (" + quote(x) + ") {")
94-
emitBlock(b)
95-
stream.println(quote(sym) + ".complete(" + quote(getBlockResult(b)) + ");")
96-
stream.println("});")
97-
case FutureFlatMap(a, x, b) => // TODO optimize
98-
emitValDef(sym, "new Promise()")
99-
stream.println(quote(a) + ".onComplete(function (" + quote(x) + ") {")
100-
emitBlock(b)
101-
stream.println(quote(getBlockResult(b)) + ".onComplete(function (v) {")
102-
stream.println(quote(sym) + ".complete(v);")
103-
stream.println("});")
104-
stream.println("});")
105-
case FutureForeach(a, x, b) =>
106-
stream.println("var " + quote(sym) + " = " + quote(a) + ".onComplete(function (" + quote(x) + ") {")
107-
emitBlock(b)
108-
stream.println("});")
109-
case FutureFilter(a, x, b) =>
110-
emitValDef(sym, "new Promise()")
111-
stream.println(quote(a) + ".onComplete(function (" + quote(x) + ") {")
112-
emitBlock(b)
113-
stream.println("if (" + quote(getBlockResult(b)) + " === true) {")
114-
stream.println(quote(sym) + ".complete(" + quote(getBlockResult(b)) + ");")
65+
case PromiseNew(maybeA) =>
66+
maybeA.fold(emitValDef(sym, "{ v: null, cs: [] }"))(a => emitValDef(sym, "{ v: " + quote(a) + ", cs: [] }"))
67+
case PromisePut(p, v) =>
68+
val i = fresh[Int]
69+
val n = fresh[Int]
70+
stream.println("if (" + quote(p) + ".v !== null) {")
71+
stream.println("throw 'This Promise has already been completed';")
72+
stream.println("} else {")
73+
stream.println(quote(p) + ".v = " + quote(v) + ";")
74+
stream.println("for (var " + quote(i) + " = 0, " + quote(n) + " = " + quote(p) + ".cs.length ; " + quote(i) + " < " + quote(n) + " ; " + quote(i) + "++) {")
75+
stream.println(quote(p) + ".cs[" + quote(i) + "](" + quote(v) + ");")
11576
stream.println("}")
116-
stream.println("});")
77+
stream.println("}")
78+
emitValDef(sym, quote(()))
79+
case PromiseFuture(p) =>
80+
emitValDef(sym, quote(p))
81+
case FutureForeach(p, f) =>
82+
stream.println("if (" + quote(p) + ".v === null) {")
83+
stream.println(quote(p) + ".cs.push(" + quote(f) + ");")
84+
stream.println("} else {")
85+
stream.println(quote(f) + "(" + quote(p) + ".v);")
86+
stream.println("}")
87+
emitValDef(sym, quote(()))
11788
case _ => super.emitNode(sym, rhs)
11889
}
11990

120-
def emitDataStructures(out: PrintWriter) {
121-
// http://jsbin.com/ebecuk/5/edit
122-
out.println("""var Promise = function (value) {
123-
| if (value !== undefined) {
124-
| this.value = value;
125-
| }
126-
| this.callbacks = [];
127-
|};
128-
|Promise.prototype.complete = function (value) {
129-
| if (this.value !== undefined) {
130-
| throw "This promise has already been completed";
131-
| } else {
132-
| this.value = value;
133-
| for (var i = 0, n = this.callbacks.length ; i < n ; i++) {
134-
| var callback = this.callbacks[i];
135-
| callback(value);
136-
| }
137-
| }
138-
|};
139-
|Promise.prototype.onComplete = function (callback) {
140-
| if (this.value === undefined) {
141-
| this.callbacks.push(callback);
142-
| } else {
143-
| callback(this.value);
144-
| }
145-
|};
146-
|""".stripMargin)
147-
out.flush()
148-
}
149-
15091
}

src/test/scala/scala/js/TestFutureOps.scala

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,61 @@ import concurrent.{Promise, Future}
66

77
class TestFutureOps extends FileDiffSuite2("test-out/") with Suite {
88

9-
trait Sleep { this: Base with FutureOps =>
10-
def sleep(delay: Rep[Int]): Rep[Future[Unit]]
9+
trait Sleep { this: Base with FutureOps with Functions =>
10+
def sleep(delay: Rep[Int]): Rep[Future[Unit]] = {
11+
val p = promise[Unit]
12+
window_setTimeout(fun(p.put), delay)
13+
p.future
14+
}
15+
def window_setTimeout(f: Rep[Unit => Unit], d: Rep[Int]): Rep[Unit]
1116
}
1217

13-
trait SleepExp extends Sleep { this: EffectExp with FutureOps =>
14-
case class Sleep(delay: Exp[Int]) extends Def[Future[Unit]]
15-
def sleep(delay: Exp[Int]) = reflectEffect(Sleep(delay))
18+
trait SleepExp extends Sleep { this: EffectExp with FutureOps with Functions =>
19+
case class WindowSetTimeout(f: Exp[Unit => Unit], d: Exp[Int]) extends Def[Unit]
20+
21+
def window_setTimeout(f: Exp[Unit => Unit], d: Exp[Int]) = reflectEffect(WindowSetTimeout(f, d))
1622
}
1723

1824
trait JSGenSleep extends JSNestedCodegen {
1925
val IR: EffectExp with SleepExp
2026
import IR._
2127

2228
override def emitNode(sym: Sym[Any], rhs: Def[Any]) = rhs match {
23-
case Sleep(delay) =>
24-
emitValDef(sym, "new Promise()")
25-
stream.println("setTimeout(function () { " + quote(sym) + ".complete(null); }, " + quote(delay) + ")")
29+
case WindowSetTimeout(f, d) =>
30+
stream.println("setTimeout(" + quote(f) + ", " + quote(d) + ");")
2631
case _ => super.emitNode(sym, rhs)
2732
}
2833
}
2934

30-
trait Ajax { this: Base with FutureOps =>
31-
val Ajax: AjaxOps
35+
trait Ajax { this: Base with FutureOps with Functions =>
36+
object Ajax {
37+
def get(url: Rep[String]): Rep[Future[String]] = {
38+
val p = promise[String]
39+
jQuery.get(url, fun(p.put))
40+
p.future
41+
}
42+
}
3243

33-
trait AjaxOps {
34-
def get(url: Rep[String]): Rep[Future[String]]
44+
val jQuery: jQueryOps
45+
trait jQueryOps {
46+
def get(url: Rep[String], f: Rep[String => Unit]): Rep[Unit]
3547
}
3648
}
3749

38-
trait AjaxExp extends Ajax { this: EffectExp with FutureOps =>
39-
object Ajax extends AjaxOps {
40-
def get(url: Exp[String]) = reflectEffect(AjaxGet(url))
50+
trait AjaxExp extends Ajax { this: EffectExp with FutureOps with Functions =>
51+
case class JQueryGet(url: Exp[String], f: Exp[String => Unit]) extends Def[Unit]
52+
object jQuery extends jQueryOps {
53+
def get(url: Exp[String], f: Exp[String => Unit]) = reflectEffect(JQueryGet(url, f))
4154
}
42-
43-
case class AjaxGet(url: Rep[String]) extends Def[Future[String]]
4455
}
4556

4657
trait JSGenAjax extends JSNestedCodegen {
4758
val IR: EffectExp with AjaxExp
4859
import IR._
4960

5061
override def emitNode(sym: Sym[Any], rhs: Def[Any]) = rhs match {
51-
case AjaxGet(url) =>
52-
emitValDef(sym, "new Promise()")
53-
stream.println("$.get(" + quote(url) + ", function (d, t, xhr) { " + quote(sym) + ".complete(xhr.responseText) });")
62+
case JQueryGet(url, f) =>
63+
stream.println("$.get(" + quote(url) + ", function (d, t, xhr) { " + quote(f) + "(xhr.responseText); });")
5464
case _ => super.emitNode(sym, rhs)
5565
}
5666
}
@@ -79,9 +89,8 @@ class TestFutureOps extends FileDiffSuite2("test-out/") with Suite {
7989
}
8090

8191
testWithOutFile("future-foreach") { out =>
82-
val prog = new Prog with EffectExp with SleepExp with FutureOpsExp with JSDebugExp with LiftString with LiftNumeric
83-
val codegen = new JSGenEffect with JSGenSleep with JSGenFutureOps with JSGenDebug { val IR: prog.type = prog }
84-
codegen.emitDataStructures(out)
92+
val prog = new Prog with EffectExp with SleepExp with FutureOpsExp with IfThenElseExp with TupledFunctionsRecursiveExp with JSDebugExp with LiftString with LiftNumeric
93+
val codegen = new JSGenEffect with JSGenSleep with JSGenFutureOps with JSGenIfThenElse with JSGenFunctions with JSGenDebug { val IR: prog.type = prog }
8594
codegen.emitSource0(prog.main _, "main", out)
8695
}
8796
}
@@ -95,9 +104,8 @@ class TestFutureOps extends FileDiffSuite2("test-out/") with Suite {
95104
}
96105

97106
testWithOutFile("future-mapflatmap") { out =>
98-
val prog = new Prog with EffectExp with AjaxExp with FutureOpsExp
99-
val codegen = new JSGenEffect with JSGenAjax with JSGenFutureOps { val IR: prog.type = prog }
100-
codegen.emitDataStructures(out)
107+
val prog = new Prog with EffectExp with AjaxExp with FutureOpsExp with IfThenElseExp with TupledFunctionsRecursiveExp
108+
val codegen = new JSGenEffect with JSGenAjax with JSGenFutureOps with JSGenIfThenElse with JSGenFunctions { val IR: prog.type = prog }
101109
codegen.emitSource(prog.main _, "main", out)
102110
}
103111
}

0 commit comments

Comments
 (0)