Last active
May 19, 2020 16:56
-
-
Save pchiusano/b1cddb3d6a6935d9eae45e3c4ae4d04a to your computer and use it in GitHub Desktop.
Distributed programming API for Unison
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| unique ability Remote loc task result g where | |
| at : loc -> '{Remote loc task result g, g} a -> task a | |
| fork : '{g} a -> task a | |
| await : task a -> result a | |
| cancel : task a -> () | |
| location : task a -> loc | |
| type Value a = Value a | |
| Remote.local.sequential.handler : Request {Remote () Value Value g} a ->{g} a | |
| Remote.local.sequential.handler = cases | |
| { a } -> a | |
| { Remote.at _loc a -> k } -> v = Value.Value !a | |
| handle k v with Remote.local.sequential.handler | |
| { Remote.await val -> k } -> handle k val with Remote.local.sequential.handler | |
| { Remote.cancel _ -> k } -> handle k () with Remote.local.sequential.handler | |
| { Remote.location _ -> k } -> handle k () with Remote.local.sequential.handler | |
| unique ability Durable.R d where | |
| restore : d a -> a | |
| unique ability Durable.W loc d where | |
| save : a -> d a | |
| saveAt : loc -> a -> d a | |
| location : d a -> loc | |
| unique ability Ephemeral.R e where | |
| restore : e a -> a | |
| unique ability Ephemeral.W pool loc e where | |
| pool : pool | |
| save : a -> e a | |
| saveAt : loc -> a -> e a | |
| location : e a -> loc | |
| retain : pool -> () |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Design notes from this first cut:
atreturning atask ais handy, but raises a question - if the task being spawned has completed with some valueaon another node, how long do we keep thatavalue around?atcould instead just return(), and then any values it returns has to be done via some sort of "remotely accessible"MVarthing. This does not seem to help, since that magicMVaris going to have the same questions.taskkeeps it alive via regular heartbeats? Once nothing running references the task, it goes away. Feels complicated but perhaps more problematic involves a lot of communication - imagine spawning millions of these logical tasks - are they all sending keepalives? Maybe the keepalives are batched. Overall feels complex, though might be the nicest API.await-ing the result still, the result is discarded. No keepalives needed. Correct usage is toawaittasks shortly after spawning them. Perhaps this is awkward to program with, but might get a long way with some helper functions. Needs some experimentation.Ephemeralis superfluous. Perhaps you can represent distributed in-memory data types just with nested tasks. See sketch below.save : a -> (loc, d a). I think this is probably less good. The location returned by thelocationoperation is not necessarily the same location that was passed tosaveAt- the durable may have migrated in the meantime.locationcan give the latest result, so tasks operating on the data can be spawned close by.awaitreturn aresult a. The thought there was to allow different handlers to supply different result types (likeEither MySpecialException a), but you really want something like a typeclass here.Here's a sketch of a simple distributed data type, a
Stream:The
Stream.savefunction materializes the stream, but not all at one location. At eachDelaynode, the remainder of the stream is scheduled onto another logical node. WhenStream.savecompletes, the entireStreamwill be in memory, spread across multiple logical nodes. Pretty epic. With a more interesting data structure thanStream, this is how you can do Spark-like workflows.OTOH, it does seem pretty easy to seems very easy to accidentally reify an infinite amount of data. (Or maybe it's exactly as easy as it is currently, like if you call
toListon any other infinite stream.) You can alwayscancelthe task.The problem with the above implementation is we don’t await the task until the stream is forced, basically its using task in place of Ephemeral. So it falls afoul of the suggestion that await should immediately follow the fork/at call.