@@ -118,6 +118,12 @@ <h2>Intro</h2>
118
118
_assert ( result instanceof Result , "Expected a result but found " + result ) ;
119
119
}
120
120
121
+ function _assert_allowed ( name ) {
122
+ if ( _globalAllowed ) {
123
+ _assert ( _globalAllowed . has ( name ) , "Not allowed to call function: " + name ) ;
124
+ }
125
+ }
126
+
121
127
class Result {
122
128
constructor ( value , exception ) {
123
129
this . _value = value ;
@@ -172,29 +178,36 @@ <h2>Intro</h2>
172
178
return newFuture ;
173
179
}
174
180
175
- static _completed ( result ) {
181
+ static _completedResult ( result ) {
176
182
var f = new CompletableFuture ( ) ;
177
- f . _internalComplete ( result ) ;
183
+ f . _completeResult ( result ) ;
178
184
return f ;
179
185
}
180
186
181
187
// fun: Exception => Future
182
188
exceptionallyCompose ( fun ) {
183
189
_assert ( _globalLanguageLevel >= 12 , "Requires Java 12+" ) ;
190
+ _assert_allowed ( "exceptionallyCompose" ) ;
184
191
return this
185
- . thenApply ( x => CompletableFuture . completedFuture ( x ) )
186
- . exceptionally ( fun )
187
- . thenCompose ( x => x ) ;
192
+ . _thenApply ( x => CompletableFuture . completedFuture ( x ) )
193
+ . _exceptionally ( fun )
194
+ . _thenCompose ( x => x ) ;
188
195
}
189
196
190
197
// fun: (Value, Value) => Value
191
198
thenCombine ( other , fun ) {
192
- return this . thenCompose ( x => other . thenApply ( y => fun ( x , y ) ) ) ;
199
+ _assert_allowed ( "thenCombine" ) ;
200
+ return this . _thenCompose ( x => other . _thenApply ( y => fun ( x , y ) ) ) ;
193
201
}
194
202
195
203
// fun: Exception => Value
196
204
exceptionally ( fun ) {
197
- return this . handle ( ( value , exc ) => {
205
+ _assert_allowed ( "exceptionally" ) ;
206
+ return this . _exceptionally ( fun ) ;
207
+ }
208
+
209
+ _exceptionally ( fun ) {
210
+ return this . _handle ( ( value , exc ) => {
198
211
if ( exc != null ) {
199
212
return fun ( exc ) ;
200
213
} else {
@@ -205,7 +218,12 @@ <h2>Intro</h2>
205
218
206
219
// fun: Value => Value
207
220
thenApply ( fun ) {
208
- return this . handle ( ( value , exc ) => {
221
+ _assert_allowed ( "thenApply" ) ;
222
+ return this . _thenApply ( fun ) ;
223
+ }
224
+
225
+ _thenApply ( fun ) {
226
+ return this . _handle ( ( value , exc ) => {
209
227
if ( exc != null ) {
210
228
throw exc ;
211
229
} else {
@@ -216,12 +234,22 @@ <h2>Intro</h2>
216
234
217
235
// fun: Value => Future
218
236
thenCompose ( fun ) {
219
- return this . thenApply ( fun ) . _dereference ( ) ;
237
+ _assert_allowed ( "thenCompose" ) ;
238
+ return this . _thenCompose ( fun ) ;
239
+ }
240
+
241
+ _thenCompose ( fun ) {
242
+ return this . _thenApply ( fun ) . _dereference ( ) ;
220
243
}
221
244
222
245
// fun: (Value, Exception) => Value
223
246
handle ( fun ) {
224
- return this . _handle ( result => {
247
+ _assert_allowed ( "handle" ) ;
248
+ return this . _handle ( fun ) ;
249
+ }
250
+
251
+ _handle ( fun ) {
252
+ return this . _handleResult ( result => {
225
253
if ( result . isValue ( ) ) {
226
254
return Result . value ( fun ( result . _value , null ) ) ;
227
255
} else {
@@ -231,15 +259,15 @@ <h2>Intro</h2>
231
259
}
232
260
233
261
// fun: Result => Result
234
- _handle ( fun ) {
262
+ _handleResult ( fun ) {
235
263
var newFuture = new CompletableFuture ( ) ;
236
264
this . _addCallback ( result => {
237
265
try {
238
266
var result2 = fun ( result ) ;
239
267
_assert_result ( result2 ) ;
240
- newFuture . _internalComplete ( result2 ) ;
268
+ newFuture . _completeResult ( result2 ) ;
241
269
} catch ( err ) {
242
- newFuture . _internalComplete ( Result . exception ( err ) ) ;
270
+ newFuture . _completeResult ( Result . exception ( err ) ) ;
243
271
}
244
272
} ) ;
245
273
return newFuture ;
@@ -252,12 +280,12 @@ <h2>Intro</h2>
252
280
if ( result . isValue ( ) ) {
253
281
var future = result . _value ;
254
282
_assert_future ( future ) ;
255
- future . _addCallback ( result2 => newFuture . _internalComplete ( result2 ) )
283
+ future . _addCallback ( result2 => newFuture . _completeResult ( result2 ) )
256
284
} else {
257
- newFuture . _internalComplete ( result ) ;
285
+ newFuture . _completeResult ( result ) ;
258
286
}
259
287
} catch ( err ) {
260
- newFuture . _internalComplete ( Result . exception ( err ) ) ;
288
+ newFuture . _completeResult ( Result . exception ( err ) ) ;
261
289
}
262
290
} ) ;
263
291
return newFuture ;
@@ -293,15 +321,15 @@ <h2>Intro</h2>
293
321
}
294
322
295
323
complete ( value ) {
296
- this . _internalComplete ( Result . value ( value ) ) ;
324
+ this . _completeResult ( Result . value ( value ) ) ;
297
325
}
298
326
299
327
completeExceptionally ( exc ) {
300
328
_assert_exc ( exc ) ;
301
- this . _internalComplete ( Result . exception ( exc ) ) ;
329
+ this . _completeResult ( Result . exception ( exc ) ) ;
302
330
}
303
331
304
- _internalComplete ( result ) {
332
+ _completeResult ( result ) {
305
333
if ( this . _result == null ) {
306
334
this . _result = result ;
307
335
}
@@ -357,7 +385,7 @@ <h2>Intro</h2>
357
385
}
358
386
359
387
for ( var i = 0 ; i < inputs . length ; i ++ ) {
360
- inputFutures [ i ] . _internalComplete ( inputs [ i ] ) ;
388
+ inputFutures [ i ] . _completeResult ( inputs [ i ] ) ;
361
389
}
362
390
363
391
if ( ! outputFuture . isComplete ( ) ) {
@@ -464,6 +492,7 @@ <h2>Intro</h2>
464
492
}
465
493
466
494
_globalLanguageLevel = lesson . maxLanguageLevel ;
495
+ _globalAllowed = lesson . allowedSet ;
467
496
468
497
var testResults = runTests ( userFunction , lesson . compiledSolution , lesson . inputs ) ;
469
498
@@ -509,6 +538,11 @@ <h2>Intro</h2>
509
538
lessondiv . append ( $ ( '<h3>' ) . text ( lesson . title ) )
510
539
lessondiv . append ( $ ( '<p>' ) . text ( lesson . text ) )
511
540
541
+ if ( lesson . allowed ) {
542
+ lesson . allowedSet = new Set ( lesson . allowed )
543
+ lessondiv . append ( $ ( '<p>' ) . text ( "Allowed methods: " + lesson . allowed . join ( ", " ) ) ) ;
544
+ }
545
+
512
546
try {
513
547
lesson . compiledSolution = compile ( lesson . solution , lesson . inputs ) ;
514
548
} catch ( err ) {
@@ -550,59 +584,82 @@ <h2>Intro</h2>
550
584
551
585
var lessons = [
552
586
{
553
- title : "Lesson 1 " ,
554
- text : `Let's start by transforming a future into a new future. ` ,
587
+ title : "Simple transform " ,
588
+ text : `Return a new future that adds a "!" to the input value ` ,
555
589
inputs : [
556
590
"input"
557
591
] ,
558
592
code : `return input.thenApply(v -> v);` ,
559
593
solution : `return input.thenApply(v -> v + "!");` ,
594
+ allowed : [ "thenApply" ] ,
560
595
maxLanguageLevel : 12 ,
561
596
} ,
562
597
{
563
- title : "Lesson 2 " ,
598
+ title : "Exception handling " ,
564
599
text : `Now it's time to handle exceptions!
565
600
Your code should do the same thing as before, but if the input future
566
601
has an exception, your result future should convert the error string
567
602
to "Error: " + exception` ,
568
603
inputs : [
569
604
"input"
570
605
] ,
571
- code : `// Hint: use the exceptionally-method
572
- return input.thenApply(v -> v);` ,
606
+ code : `return input.thenApply(v -> v);` ,
573
607
solution : `
574
608
return input
575
609
.thenApply(v -> v + "!")
576
610
.exceptionally(e -> "Error: " + e.getMessage());` ,
611
+ allowed : [ "thenApply" , "exceptionally" ] ,
577
612
maxLanguageLevel : 12 ,
578
613
} ,
579
614
{
580
- title : "Lesson 3: Combining futures" ,
581
- text : `Now we are going to get two futures as input. Your job is to return a future that is the concatenation of the values.` ,
615
+ title : "Combining futures" ,
616
+ text : `Now we are going to get two futures as input. Your job is to return a future that is the concatenation of the values. You are only allowed to use thenCombine ` ,
582
617
inputs : [
583
618
"first" , "second"
584
619
] ,
585
- code : `// Hint: use thenCombine
586
- return null` ,
620
+ code : `return null` ,
587
621
solution : `
588
622
return first.thenCombine(second, (a,b)->a+b);` ,
623
+ allowed : [ "thenCombine" ] ,
624
+ maxLanguageLevel : 12 ,
625
+ } ,
626
+ {
627
+ title : "Combining futures: part 2" ,
628
+ text : `Same as previous, but now you have to solve it only using thenCompose and thenApply` ,
629
+ inputs : [
630
+ "first" , "second"
631
+ ] ,
632
+ code : `return null` ,
633
+ solution : `
634
+ return first.thenCompose(a -> second.thenApply(b -> a+b));` ,
635
+ allowed : [ "thenCompose" , "thenApply" ] ,
589
636
maxLanguageLevel : 12 ,
590
637
} ,
591
638
{
592
- title : "Lesson 4: Combining many futures" ,
639
+ title : "Combining multiple futures" ,
593
640
text : `Now we are going to get three futures as input. Your job is to return a future that is the concatenation of the values.` ,
594
641
inputs : [
595
642
"first" , "second" , "third"
596
643
] ,
597
- code : `// Hint: use thenCompose and thenApply
598
- return null` ,
644
+ code : `return null` ,
599
645
solution : `
600
646
return first.thenCompose(a -> second.thenCompose(b -> third.thenApply(c -> a + b + c)));` ,
601
- // Alternative: first.thenCombine(second, (a,b)->a+b).thenCombine(third, (a,b)->a+b)
647
+ allowed : [ "thenCompose" , "thenApply" ] ,
648
+ maxLanguageLevel : 12 ,
649
+ } ,
650
+ {
651
+ title : "Combining multiple futures: part 2" ,
652
+ text : `Same as before, but now you have to use thenCombine and produce intermediate values` ,
653
+ inputs : [
654
+ "first" , "second" , "third"
655
+ ] ,
656
+ code : `return null` ,
657
+ solution : `return first.thenCombine(second, (a,b)->a+b).thenCombine(third, (a,b)->a+b)` ,
658
+ allowed : [ "thenCombine" ] ,
602
659
maxLanguageLevel : 12 ,
603
660
} ,
604
661
{
605
- title : "Lesson 5: Retry on exceptions" ,
662
+ title : "Retry on exceptions" ,
606
663
text : `Now we are going to simulate a retry on exception. To simplify things, the retry logic is represented by the future called retry.` ,
607
664
inputs : [
608
665
"input" , "retry"
@@ -611,10 +668,11 @@ <h2>Intro</h2>
611
668
return null` ,
612
669
solution : `
613
670
return input.exceptionallyCompose(exc -> retry);` ,
671
+ allowed : [ "exceptionallyCompose" ] ,
614
672
maxLanguageLevel : 12 ,
615
673
} ,
616
674
{
617
- title : "Lesson 6: Retry on exceptions" ,
675
+ title : "Retry on exceptions: part 2 " ,
618
676
text : `Same as the previous lesson, but now you are not allowed to use methods defined in Java 12+.` ,
619
677
inputs : [
620
678
"input" , "retry"
@@ -625,6 +683,7 @@ <h2>Intro</h2>
625
683
return input.thenApply(v -> CompletableFuture.completedFuture(v))
626
684
.exceptionally(exc -> retry)
627
685
.thenCompose(f -> f);` ,
686
+ allowed : [ "thenApply" , "exceptionally" , "thenCompose" ] ,
628
687
maxLanguageLevel : 11 ,
629
688
} ,
630
689
] ;
0 commit comments