Promises implemented in Snap!

I have implemented promises in Snap!.

What are Promises?

In short, a promise represents a computation that is currently running. A script can launch a long, complex computation, get a promise for that computation, do whatever it wants while the computation is running, and then check to see if the computation is finished, and the result of the computation if so, at any time.

This project implements promises in Snap! directly, without using the Javascript implementation of promises.

A promise is implemented as a list that contains information about whether the computation is completed and what result it has reported. The first item of the list is simply the word "promise", used to identify it. The second item of the list is true if the computation has completed, and the third item of the list is the result.

When you create a promise, it returns a new list and launches the computation in a new thread. When the computation finishes, the new thread modifies the returned list, marking it as completed, and setting the result.

Unfortunately, if your computation doesn't 'wait' at all, allowing other scripts to run, then the computation takes up way too much time compared to everything else that needs to run, and Snap! will slow to a crawl.

What do you think? Is there any way I could improve on them - and do you think they'll come in handy for your projects?

Could you make an example simple project that shows the blocks in action please?

(I've never got my head around promises!)

I have hastily implemented an example in the project. I hope it suffices - if not, however, I'd be happy to explain them to you.


Am I right in thinking that there is no "the procedure" and that the variable isn't used at all?


This is a minor issue, but I can teach you an old-time Lisp trick about type tags such as the word "promise." While unlikely, it's possible that a list might happen to start with the word "promise." So, instead, untitled script pic (2), use untitled script pic when creating a promise, and (the key point) use untitled script pic (1) rather than = to check for the tag.

Nice, simple code. Amazing, isn't it, what you can do with first class procedures?

In your demo program you can see the "dancing" sprite speed up and slow down every so often, which I take as an indication of when the other thread is working.

Take a look at the "Streams" library (third in the list). It implements Scheme promises, which have a different semantics from the Javascript-style ones you've implemented. Namely, in Scheme the whole object of the exercise is that sometimes you don't need to "cash in" a promise at all, which saves a lot of time. And in fact it lets you build infinite lists! Because in a finite time, your program can't cash in the infinitely many promises involved, so your program never finds out that the whole infinite list isn't sitting there in the computer's memory. In the library there's a procedure that generates the entire infinite list of all the prime numbers, using the Sieve of Eratosthenes.

Yes. Sorry about that - that was meant to show the prime-calculating custom block that returns a promise, but something weird seems to have happened to it. I've removed that variable from the project.

OK - so - lets see if I understand promises now - as how you've coded them anyway :slight_smile:

They look as if designed for long running functions where you don't want stop and wait for them to finish now before you carry on with something else.

You can do some other stuff in the meantime and then start checking later on to see if its finished.

That's actually a pretty cool trick. I think I've thought about something like this before, but I didn't think of it while I was making this! I'll make sure to add it in.

Yes indeed.

Now that you mention it, the sprite does seem to be speeding up and slowing down slightly. I guess you're right.

Ooh - I hadn't fully considered that there was a connection between lazily evaluated streams and promises while I was making this project.

Correct - at least for my implementation. A lazier implementation could be used for infinitely long streams whose values only get calculated when you try to use them, as @bh mentioned.

Am I right in thinking that the concurrency exhibited in this implementation is implicitly based on the way that Snap! 'scheduling' treats looping constructs?

Yes - this version of promises doesn't implement any concurrency on its own, it simply launches a new Snap! thread for the computation.

Nice. Is the JS bit really important? I don't quite see why. Did you get the idea from ambeval in SICP? You don't really use the failure continuation for anything in your example.

Can you clarify what JS bit you're talking about?

I never read SICP.

--> (that statement) -->
(my head)

He's talking about the "reject" variable. It's not actually a continuation in the literal sense, since you've just defined it as a regular procedure, but invoking it does allow any scripts that were waiting on that promise to continue.

It's not used in any of your examples - perhaps you could add an example where the Pinky Promise is rejected, just so we can see how to do it, and perhaps a possible use case for it?

Edit: In addition, perhaps there's a way you could do away with the Javascript in your implementation entirely? Snap! projects are usually easier to understand if you avoid Javascript whenever possible.

Can you post a pic of where you are seeing JS - its not leaping out at me

I was talking about this procedure in @snapenilk's implementation of promises.

I've come to the realization that the Javascript here only serves to ensure that settled promises is not tampered with by accident. While I personally think people should be able to tamper with the values that my projects produce without having to go into the Javascript, I understand this decision.

@snapenilk - maybe you could create a new procedure, something like "force (list) to become an immutable array" for this purpose? It might come in handy in other projects that produce lists that you don't want people to tamper with accidentally, and it would help to separate the Javascript (which not everyone might understand) from the actual implementation itself.

Oh, it is! It's not the Snap! evaluator's internal continuation, but you can make your own continuations -- see the discussion of Continuation Passing Style in the Snap! manual.

Oh you should! There are two kinds of programmers: Ones who've read SICP, and muggles.

I added JavaScript comments to the function; does that help?

In my Snap! Dev tools project, I made a block that converts a list to an array.
In my Objection project, I made a reporter that freezes objects.

And I just made a block that locks a list after converting it to an array.