This project is to add ECMAScript-7-style "await" syntax to Iced Coffeescript.
If you're interested in doing this project, take a crack at the pre-project, and submit a link to your fork as part of your project proposal.
Pencil Code is about empowering novice coders, and one of the classing beginner coding projects is Guess My Number.
Here it is, in CoffeeScript
(together with the Pencil Code library
for random
and write
):
secret = 1 + random 100 for count in [5..1] write "Guess my number! #{count} guesses left." g = prompt 'Your guess?' if g > secret then write "Nope, smaller!" if g < secret then write "Nope, bigger!" if g is secret write "You got it!" break write "The number was #{secret}"
Should the first thing we teach students be
prompt
? That's terrible - prompt
is unlike anything else in Javascript. It blocks the thread
and pops up a window. You cannot customize it to make, say,
a "nubmers only" input, and when it pops up, it prevents
students from interacting with the page, invariably covering
up messages like "bigger" or "smaller" that the student
has printed on the screen. Try using
it, pretending to be a student. It's OK, but not great.
But if we create a custom readnum
function with
a better UI for students to use, we cannot block the UI thread,
so to wait for the user to finish entering an answer,
readnum
must use a callback argument. With a
callback, Guess My Number would look like this:
secret = 1 + random 100 count = 5 startloop = -> write "Guess my number! #{count} left." readnum 'Your guess?', finishloop finishloop = (g) -> if g > secret then write "Nope, smaller!" if g < secret then write "Nope, bigger!" count -= 1 if g is secret write "You got it!" count = 0 if count is 0 leaveloop() else startloop() leaveloop = -> write "The number was #{secret}" startloop()
This program is obviously much harder to understand and write. Using a callback means the programmer needs to plan and create two or three functions that call each other. It is totally out of reach of a first-time programmer.
And await
solves this.
The Coffeescript world currently has an implementation of
await
that addresses this problem, and it is close,
but not quite simple enough for classroom use. The implementation
was done by Max Krohn in his fork Iced Coffeescript (ICS), and
it lets the program look like this:
secret = 1 + random 100 for count in [5..1] write "Guess my number! #{count} guesses left" await readnum 'Your guess?', defer g if g > secret then write "Nope, smaller!" if g < secret then write "Nope, bigger!" if g is secret write "You got it!" break write "The number was #{secret}"
The ICS compiler automatically pivots Program 3 so that it turns in to Program 2. This transformation is called the "Continuation Passing Style" transformation; it is a classical functional programming concept.
Pencil Code uses Iced Coffeescript to allow beginners wait for asynchronous operations without writing callback functions.
Unfortunately, the ICS syntax confuses beginners. In our attempts
to teach with it, we have found that beginners do not understand that
defer g
creates a continuation function that is being
passed. For example, they cannot figure out "why they need a comma"
before the word defer
if it's a second argument.
Teachers have suggested that a better syntax would be to eliminate
defer
and use this:
secret = 1 + random 100 for count in [5..1] write "Guess my number! #{count} guesses left" g = await readnum 'Your guess?' if g > secret then write "Nope, smaller!" if g < secret then write "Nope, bigger!" if g is secret write "You got it!" break write "The number was #{secret}"
Interestingly, this syntax is exactly what is being proposed for
the next version of Javascript in the ECMAScript 7 committee. In ES7,
await
will be an operator that causes flow to wait for
a promise to finish.
In order to use an ES7 await, you cannot use a regular function that hakes a callback function as an argument. Instead, you need to use a standard callback convention: you need to return an ES6 Promise. Promises are the future of asynchronous code; both ES6 and ES7 encourage everybody to use the same pattern.
Another interesting fact is that the proposed syntax can coeexist
with existing Iced Coffeescript syntax: if an await
block
does not contain a defer
expression, then we can safely
assume that it is an ES7-Promise-style await. A very rough prototype
showing a working syntax that is unabmiguous for Coffeescript's LALR(1)
parser has been
done
in a branch of ICS here. It limits Promise-style awaits to
be used in top-level assigments.
This project is to implement the ES7-style await syntax in ICS for use by begnning students. The branch above has a rough start at an implementation. Several things are needed beyond the code above:
await
on a non-asynchronous function).
async function
, returning a
Promise
instead of an immediate result. This is analogous
to the ICS autocb
feature, and could be implemented as an
adaptation of that existing code. You might decide to do this
transformation automatically, or alternately introduce new operators
for async functions, for example ==>
or -->
.