Status Report - draft APIs for Promise, WebSockets, and Geolocation

551 views
Skip to first unread message

Evan Czaplicki

unread,
Jan 29, 2015, 12:07:42 PM1/29/15
to elm-d...@googlegroups.com
My goals for promises are to get the core API worked out, and then see how that works for Http, WebSockets, and Geolocation. So far there's been a nice feedback loop that's driven things forward.

Here are the APIs as they are at the moment:
  • Promise - API is pretty much predetermined, seems fine to me
  • WebSockets - listen function could maybe be better
  • Geolocation - relatively happy with this one
  • Http - still needs a bunch of work for the general case. I like get and post though.
These APIs all work by going and running some effect, and then telling your program about a result through a "channel" just like in elm-html event handlers. This means the Signal.Channel stuff is changing a bit as well:
  • Signal - I think everything we have now (Signal.Message, local-channel, etc.) can be created in terms of these primitives.
I don't know the right name for channel / input / handle / whatever, but that's not the goal at this point. The important part is the Signal.send function, which lets all of these promise APIs talk to your program as the results of effects come in.

I think implementation will be quite simple for all of these, so I have not done a ton on that yet.

Hassan Hayat

unread,
Jan 29, 2015, 1:51:28 PM1/29/15
to elm-d...@googlegroups.com
This looks very promising (pun very much intended). I like the Geolocation API and think it should be part of the standard library. 

I think that Promises will finally get us to cover most of the Web APIs (like getUserMedia) and provide simple libraries for that. I think that your geolocation example proves that. I'm looking forward to playing around with it. Just take your time with the implementation, I think you're on the right track.

Evan Czaplicki

unread,
Jan 29, 2015, 3:05:26 PM1/29/15
to elm-d...@googlegroups.com
This looks very promising (pun very much intended). I like the Geolocation API and think it should be part of the standard library. 

Haha, thanks :)

Anyone else share this feeling about geolocation? I wasn't really sure, and breaking it out felt more cautious. I figured there'd be a bunch of things like this, and they can sometimes feel low-level. One day, I can imagine people wrapping them up in some way or another that's nicer, and it'd be a shame to take the name Geolocation from everyone ever. Not really sure though!
 
I think that Promises will finally get us to cover most of the Web APIs (like getUserMedia) and provide simple libraries for that. I think that your geolocation example proves that. I'm looking forward to playing around with it. Just take your time with the implementation, I think you're on the right track.

Yeah, I'm excited to start covering more and more with this :) Thanks again! :D

Rehno Lindeque

unread,
Jan 29, 2015, 4:02:14 PM1/29/15
to elm-d...@googlegroups.com
Anyone else share this feeling about geolocation? I wasn't really sure, and breaking it out felt more cautious. I figured there'd be a bunch of things like this, and they can sometimes feel low-level. One day, I can imagine people wrapping them up in some way or another that's nicer, and it'd be a shame to take the name Geolocation from everyone ever. Not really sure though!

I like having separate libraries personally; it's not fun waiting for lots of things you don't use to compile... Everything does look very cool, I'm eager to see more of how the signals tie in!

Dylan Sale

unread,
Jan 29, 2015, 4:28:29 PM1/29/15
to elm-d...@googlegroups.com

I have been thinking about future portability of Elm to other platforms which may or may not have geolocation capabilities.

I would prefer the standard library just have things that are guaranteed to be there on all platforms.


On Fri, 30 Jan 2015 07:32 Rehno Lindeque <rehno.l...@gmail.com> wrote:
Anyone else share this feeling about geolocation? I wasn't really sure, and breaking it out felt more cautious. I figured there'd be a bunch of things like this, and they can sometimes feel low-level. One day, I can imagine people wrapping them up in some way or another that's nicer, and it'd be a shame to take the name Geolocation from everyone ever. Not really sure though!

I like having separate libraries personally; it's not fun waiting for lots of things you don't use to compile... Everything does look very cool, I'm eager to see more of how the signals tie in!

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Joey Eremondi

unread,
Jan 29, 2015, 4:39:28 PM1/29/15
to elm-d...@googlegroups.com

I agree. I'd prefer some specific like that be a library. Having really solid, useful things will help promote the libraries, so people know it's not just for experimental stuff.

Hassan Hayat

unread,
Jan 29, 2015, 5:02:12 PM1/29/15
to elm-d...@googlegroups.com
I don't know about having GeoLocation as a standard library hurting portability. I mean Mouse, Keyboard, and Touch are all in the standard library and not all devices have all three. Actually, very very few devices have all three.

Plus, I think that if the current efforts of compiling Elm through Google Closure pays off, we won't ever need to worry because if you don't use that API, it will simply never get called or even end up in your JS. 

Perhaps, as a compromise, we should do something like Dart. They have "dart:core" which is their "core" library. This is the sort of stuff that runs absolutely anywhere, from Servers to Browsers. This has stuff like numbers, strings, lists, maps, etc... Then they have "dart:html" which is their html library which exposes all the dom functionality, from all the dom nodes, to css, websockets, geolocation, etc... And then they have additional libraries to provide needed abstractions and additional functionality like "dart:web_audio", "dart:web_gl" or "dart:svg". The reason they do this is so that they separate out "dart:html" from "dart:io" which is their server-side library. 

But the important thing about Dart is that when you get it, you get all those libraries, even the stuff not in "dart:core". It's kinda their selling point. They use the term "batteries included" in their marketing material.  

But this all sounds like a discussion for another time. Perhaps it's best to just wait for the Promises. From there, most of the Web APIs can get into Elm natively, and then we can see how those libraries are structured and called.

Rudolf Adamkovič

unread,
Jan 29, 2015, 7:15:37 PM1/29/15
to elm-d...@googlegroups.com
Great points!

+1 for having a small core library with just numbers, string, etc.

R+

Sent from my Mac

Jeff Smits

unread,
Jan 30, 2015, 3:36:47 AM1/30/15
to elm-discuss
APIs in general: LGTM
Http.send: Maybe don't curry it, use a record instead?

Promise mapN implementation: Can you rename these to seqMapN? They evaluate the Promise arguments to map in the sequence they were given in, which isn't a normal property of function application/map. Or is it in a strict language? 
I'd do something like:
seqmap2 func promiseA promiseB =
promiseA
`andThen` \a -> promiseB
`andThen` \b -> succeed (func a b)

map2 =
Native.Promise.map2

apply =
map2 (<|)

map3 func promiseA promiseB promiseC =
map func promiseA
`apply` promiseB
`apply` promiseC

sequence promises =
case promises of
[] ->
succeed []
promise :: remainingPromises ->
seqmap2 (::) promise (sequence remainingPromises)

interleave =
case promises of
[] ->
succeed []
promise :: remainingPromises ->
map2 (::) promise (interleave remainingPromises)

--

Evan Czaplicki

unread,
Jan 30, 2015, 4:46:48 AM1/30/15
to elm-d...@googlegroups.com
Promises let you have effects, interleaving file actions, sending messages, or printing stuff is not a good default. Is this a controversial idea? What is the behavior of liftM3 for IO in Haskell?

Once concurrency stuff is done, it'll be easy to make something like Promise.Interleave in which everything is like you say. I think people should opt in to that though.


--
Sent from Gmail Mobile

Jeff Smits

unread,
Jan 30, 2015, 7:55:39 AM1/30/15
to elm-discuss
Yes, higher arity liftM in Haskell "[scan] the monadic arguments from left to right". Yes this makes total sense to have this as a default, it's not controversial. But no, you should still not call these mapN.

map is definitely a more friendly name than lift, but mapN in all the libraries so far are like higher arity liftA in Haskell. For an Applicative Functor (sorry for the jargon) lifts have no execution order of the arguments. I think it's a bad idea to overload mapN as usually being a (zippy, if there's a choice) applicative and sometimes being monadic. That's why I'm proposing seqMapN or sequentialMapN for the monadic lift. The documentation can then clearly state that you should default to seqMapN, or you can simply not implement mapN and only have an explicit interleave function. Or have a submodule Promise.Parallel where you define the mapN functions. That should giev a good enough hint.

Janis Voigtländer

unread,
Jan 30, 2015, 11:09:11 AM1/30/15
to elm-d...@googlegroups.com

I think this is a misunderstanding:

For an Applicative Functor (sorry for the jargon) lifts have no execution order of the arguments.

There is a difference between Applicative and Monad, but the difference is not that one has an “execution order” whereas the other does not. Parsers are often Applicatives, and liftA2 then is sequential composition of parsers (“first parse this, then parse the remaining unconsumed input as that“).

Janis Voigtländer

unread,
Jan 30, 2015, 11:10:15 AM1/30/15
to elm-d...@googlegroups.com
Or I may be misunderstanding what you were trying to say with that sentence.

Janis Voigtländer

unread,
Jan 30, 2015, 11:24:00 AM1/30/15
to elm-d...@googlegroups.com

If what you were trying to say is that for a (law-abiding) Applicative and, say, its liftA2 function, the equivalence

liftA2 f x y
 ==
liftA2 (flip f) y x

holds, then that is wrong.

Jeff Smits

unread,
Jan 30, 2015, 12:16:43 PM1/30/15
to elm-discuss

That's exactly what I meant. I think I'm confused by the analogue monad vs applicative = sequential vs parallel. I apologise for the misinformation. I wonder if there is any real difference between liftA and liftM then.

Janis Voigtländer

unread,
Jan 30, 2015, 12:29:53 PM1/30/15
to elm-d...@googlegroups.com

No problem, good to clear that up in case it has an impact on the naming discussion.

Aside: that equivalence does hold for liftM2 if the monad is what they call a commutative monad. The difference between Applicative and Monad is less about parallel vs. sequential, more about the presence or absence of context-dependency (using information from inside one computation to influence the choice of another computation). For the parser case, it so happens that if one only uses the Applicative part, one characterizes exactly all context-free grammars.

Evan Czaplicki

unread,
Jan 30, 2015, 12:30:26 PM1/30/15
to elm-d...@googlegroups.com
I bet the root is related to the ApplicativeDo proposal in Haskell? The idea is that when parallelism is an option (<$>) and (<*>) can lead to faster code than (>>=) but it depends on the particular instance. For example, Haxl is all about doing things in parallel, so it's applicative instance allows it (iirc the monad instance does not, meaning it doesn't actually follow all the laws). Other than that case, I'm not really aware of other things that do things in parallel by default.

Overall, I think all of those details are disconnected from the term "map". It can mean many things, we are the deciders :P

Richard Feldman

unread,
Jan 30, 2015, 12:37:18 PM1/30/15
to elm-d...@googlegroups.com
Regarding the Http general case, I think a good place to look would be at fetchhttps://github.com/github/fetch

Basically it's the new standard intended to replace XMLHttpRequest, and it looks like a pretty nice API. Basing Elm's Http.send (or maybe Http.fetch?) on that could simplify things.

Evan Czaplicki

unread,
Jan 30, 2015, 1:24:55 PM1/30/15
to elm-d...@googlegroups.com
Richard, do you know of any document that actually defines the API rather than showing examples? I have not been able to find anything, and it's hard to reverse engineer it all from examples.

I just checked in my progress from today which is almost everything that XMLHttpRequest can do. Not sure if I ended up close to fetch though, but I really want to be able to compare.

--

Richard Feldman

unread,
Jan 30, 2015, 2:15:32 PM1/30/15
to elm-d...@googlegroups.com
Unfortunately I'm not aware of one...even the official API section is just an example: https://fetch.spec.whatwg.org/#fetch-api

Looking at that GitHub polyfill is probably instructive, although obviously not what you're looking for. :)

Dandandan

unread,
Jan 30, 2015, 4:32:22 PM1/30/15
to elm-d...@googlegroups.com
Looks quite pretty overall! Great work :) Some questions though:

The Geolocation functions have the following types annotations:

getCurrentPosition : (Position -> Promise x a) -> (Error -> Promise y b) -> Options -> Promise z ()
watchPosition : (Position -> Promise x a) -> (Error -> Promise y b) -> Options -> Promise z Int
clearWatch : Int -> Promise x ()

Why don't they have something like this type instead:

getCurrentPosition : Options -> Promise Error Position
watchPosition : Options -> Promise Error (Position, Int)

I imagine using the Promise functions map/catch/andThen, or am I missing something here? And what error is the clearWatch supposed to return?

Sebastian Graf

unread,
Jan 30, 2015, 5:43:12 PM1/30/15
to elm-d...@googlegroups.com
Regarding the Promise API: The API itself looks fine to me, but could you please make the applicative functions work in parallel?
As it is now, map2 just fetches its two promises one after another whereas for the case where the second promise doesn't depend on the result of the first promise (i.e. doesn't need the full monad package), the two promises can and should be fetched in parallel.
Correct me if this would hurt some of the Applicative rules.

Paul Chiusano

unread,
Jan 31, 2015, 12:33:26 AM1/31/15
to elm-d...@googlegroups.com
Looks great! When do you think there will be an implementation we can start playing with? I am anxious to try it out, it adds a lot of expressiveness and will probably make a number of things easier.

Also is the API decided for creating channels (or whatever they are to be called) and for running a `Signal (Promise x a)`?

Paul :)

Janis Voigtländer

unread,
Jan 31, 2015, 4:29:46 AM1/31/15
to elm-d...@googlegroups.com

Sebastian, have you seen this message and the following ones?

Assuming that a promise “that doesn’t need the full monad package”, by being an Applicative, would automatically allow parallelism is wrong. There are Applicatives for which the equivalence

liftA2 f x y
 ==
liftA2 (flip f) y x

does not hold.

So, concerning your question whether this would hurt some of the Applicative rules: The real question should be whether or not it follows from the Applicative rules (because only if it would always hold, you could do the desired “parallelism optimization” for all Applicatives). And the answer is No.

Alternatively, maybe you actually want to ask a different question, where Applicative is replaced by something like “a commutative Promise“. Problem is: Whereas Monad and Applicative can easily be distinguished by just their interfaces (“Is there a >>= function or not?”), it’s not possible to tell just from the types / available operations whether or not a Promise or Monad or Applicative is commutative in the required sense.


--

Sebastian Graf

unread,
Jan 31, 2015, 6:23:28 AM1/31/15
to elm-d...@googlegroups.com

Hi Janis,

yes, I read them and smelled the strong relation to my issue.

To wrap up what I think you are saying: The parallelization optimization should not be part of the Applicative API because it assumes more about the structure of Promise than it should? Isn’t this the same as saying every applicative functor should be a kind-of free applicative functor (e.g. exploit nothing more about the structure than given by the laws)? Note that I’m not entirely firm with the concepts I just threw out, so please correct me.

One way or the other, there should be some functions in the API which make use of this optimization, even if they aren’t part of the inofficial Applicative API (e.g. mapN). Maybe we could call them fetchN or just one function fetch{Parallell|Many} : [Promise a] -> Promise [a], which would be akin to sequenceA.

Evan Czaplicki

unread,
Jan 31, 2015, 7:30:56 AM1/31/15
to elm-d...@googlegroups.com
Discussion of "applicative, parallelism, etc." please start another thread, seems fairly tricky and valuable to many folks. Janis, I think it's possible to make the point in a less jargon-y way. Promises allow arbitrary effects, and ordering effects is very very important in some cases, such as printing out to the command line. If our default is "whatever random order!" that'll confuse many many people. If we have a secondary version that allows concurrent work, everyone is happy.

Paul, not sure about ETA but I'll post on the list as soon as it's ready. As for inputs and sending messages to those inputs, see this document. The most relevant part are this and this which answer the questions "how do you send values in?" and "how do you send promises out?"

Evan Czaplicki

unread,
Jan 31, 2015, 9:13:21 AM1/31/15
to elm-d...@googlegroups.com
Dan, can you start a thread about geolocation? You may be right, but there are some details of the root API that make things more complex (no response until the user clicks "Give access to location" button, so delays can be arbitrarily long). Would be good to brainstorm further!

Paul Chiusano

unread,
Jan 31, 2015, 10:50:11 AM1/31/15
to elm-d...@googlegroups.com
Evan, thanks, I just read through that doc and am looking forward to the implementation. I think there's a way to make defining "handlers" more flexible and require fewer new constructs, I posted a detailed comment on the gist. I'd be interested to get your thoughts.

Also, I very much agree that since promises are to be used for arbitrary effects, and ordering them is important, better to keep sequential execution for mapN. People can always opt in to allowing the nondeterminism with some other functions (parMapN?).

Paul :)

John Mayer

unread,
Jan 31, 2015, 5:50:02 PM1/31/15
to elm-d...@googlegroups.com
It took me a while to understand where the Signals went. Now I think I get it, and I like it.

Correct me if I'm wrong, but now anytime we have side effects, we need to go through an output - this deprecates the style "Signal request -> Signal response" (like the old HTTP) forcing all such stuff to explicitly go through inputs/outputs.

Paul Chiusano

unread,
Jan 31, 2015, 6:07:21 PM1/31/15
to elm-d...@googlegroups.com, john.p....@gmail.com
I just updated my comment https://gist.github.com/evancz/fe479a6478b85ff9aee0#comment-1384209 with an answer to your question, but basically, I think it's easy to reproduce the old Http API in terms of these primitives.

Paul :)

Evan Czaplicki

unread,
Jan 31, 2015, 6:13:45 PM1/31/15
to elm-d...@googlegroups.com, John Mayer
John, yeah, all effects are handled "outside" so the (Signal a -effect> Signal b) goes away.

I want people to get in the mindset of "push all the effects to the outside" and think about modeling things inside the program. So instead of writing a promise, it's better to make a data structure that can be analyzed and turned into a promise later. For example, if you need to make 40 server requests, the "push it to the outside" makes it way easier to batch these and remove duplicates.

I'll be writing more about this as it gets closer!

John Mayer

unread,
Jan 31, 2015, 6:25:21 PM1/31/15
to elm-d...@googlegroups.com
Ok, I'm hearing Paul, and the jury is still out for me. We don't always need the loop behavior (pop the results of the promise to the top of the signal graph as an Input) and so this whole handler thing is going to be a lot more verbose for a few of these scenarios.

This is just my initial reaction, but it doesn't feel like "fun Elm FRP" anymore and that's kinda worrying to me. 

(Of course there's nothing stopping Paul from implementing his extensions as a native community library)

John Mayer

unread,
Jan 31, 2015, 6:32:27 PM1/31/15
to elm-d...@googlegroups.com
Actually a real concern is how does FIFO work? Kind of important for HTTP. That's a legitimate non-stylistic gap.

Evan Czaplicki

unread,
Jan 31, 2015, 7:34:20 PM1/31/15
to elm-d...@googlegroups.com
This is just my initial reaction, but it doesn't feel like "fun Elm FRP" anymore and that's kinda worrying to me. 

I agree very much about this point. Promises overall feel fancy. Introducing new syntax feels fancy. There's just a lot of stuff here that wasn't needed before.

My goal is to get a solution that works in general, but that's step one in making something fit for releasing! I think this feature will have a much longer experimentation and review phase than past changes.

Hassan Hayat

unread,
Jan 31, 2015, 7:45:05 PM1/31/15
to elm-d...@googlegroups.com
I agree. I think it's best not to introduce this whole Promise stuff wholesale as a release just yet, but let everyone play around with it, see what's good and bad, where the problems occur, what's missing, and figure out the best ways to improve it from using it. 

Elm before just had values, functions, signals, and ports. Now it adds a 5th thing. I think extra care should be taken when introducing a whole new concept.

Maybe we could put it under a flag in the new release. I saw that when Dart introduced async/await, they put async/await behind a compiler flag for two whole releases until they got the feedback they needed.

The release notes can say something like "Note: Experimental API currently under development and behind a flag...."

Max Goldstein

unread,
Feb 2, 2015, 10:42:53 PM2/2/15
to elm-d...@googlegroups.com
Okay, maybe I'm late to the party here, but how do I escape a promise? Obviously I can't get a value out of a promise, but a signal sounds reasonable. Either the promise has not yet been fulfilled, succeeded, or errored; sounds like a union type much like the old Response. Apologies if this has already been answered.

I'm thinking of the classic 2d graphics program: define a single frame as a pure function, then lift that in main with all the signals it needs. If I want to visualize csv data, then I need a signal of that data loading.

Hassan Hayat

unread,
Feb 11, 2015, 8:47:53 AM2/11/15
to elm-d...@googlegroups.com
In the spirit of your (Evan) APIs (using promises), and after hearing some complaints in the community about the lack of caching ability in Elm, I just came up with this quick draft API for manipulating the browser cache. No implementation here, just types. Hopefully, we'll be able to add this one to list of APIs that magically work when Promises are released.

https://github.com/TheSeamau5/storage/blob/master/src/Storage.elm

Oh, by the way, the idea is to have it wrap Mozilla's LocalForage since it's awesome and polyfills IndexedDB, WebSQL, and LocalStorage so it works on all browsers and it's async and has support for es6 promises. 
Reply all
Reply to author
Forward
0 new messages