Promise.andThen signature with disjunction types
bblfish opened this issue · 12 comments
Currently andThen
is defined
def andThen[B](onFulfilled: js.Function1[A,B]): Promise[Any] = js.native
This loss of type in the response (it's of type Promise[Any]
rather than Promise[B]
makes it awkward when writing functions using the Fetch
api where one would like to write things like
def fetchListener(e: FetchEvent) = e.respondWith {
fetch(e.request.url).andThen{
response=> f(response),
err => err
}
}
where respondWith takes a Promise[Response]
. With the current signature one needs to add an extra cast.
Instead the following signature seems more correct and closer to the JS documentation I have read
def andThen[B](onFulfilled: js.Function1[A,B]|js.Function1[A,Promise[B]]): Promise[B] = js.native
Here we use the disjunction type since andThen
can either take a function to a value or a function to something Promisy.
Hmm. I've thought a little more about it and wouldn't a more correct typing would be:
def andThen[B](onFulfilled: js.Function1[A,B|Promise[C]]): Promise[B|C] = js.native
Since you could have a promise that conditionally returns either a value or a promise?
This would probably stack up quite quickly over several promises into some fairly nasty signatures :(
yes, you reveal an interesting possibility opened by the andThen function, namely that it can pass a function which on some input would return immediately and on others returns a promise that may be running on some other worker thread perhaps...
Note you'd either:
- need to put the
C
in theandThen
method so that it is writtenandThen[B,C]...
- or you could have one type variable like this:
def andThen[B](onFulfilled: js.Function1[A,B|Promise[B]]): Promise[B] = js.native
and the programmer could use an either type when needed. Eg if a user is writing code that actually had a function that returns aPromise[C]
or aD
she could use the the function like this
fetch(url).andThen[CachedResponse|RemoteResponse]{
case Response(401,headers) if headers.contains(`WWW-Authenticate`) => fetch(signed(url))
case r@Response(200,_) => r
}
Assuming that CachedResponse
and RemoteResponse
don't for some reason have a Response
trait in common.
reason for current API returns [Any]
: #101 (comment)
Ah ok, I propose to add that information about potentially infinitely nested Promises to the method documentation, as that is something that most developers would rarely end up working out for themselves. ( at least not run of the mill scala devs... ). Otherwise the danger is that someone transforms this andThen
to a scala.Future
without realising that this type of squashing may be appearing in the background...
Aside: Are there type systems that could deal with that?
Promises will likely be provided by the Scala.js core library in the next release (and include slightly better typing than currently provided, with caveats). You can track the progress here.
@mseddon Now they are. Should we directly migrate scala-js-dom to scalajs-0.6.7 or should we keep the scala-js-dom Promise until a later "major" release?
I think we should immediately migrate to js.Promise
, then immediately publish 0.9.0.
@danielwegener I strongly agree with @sjrd here, we want to encourage people to move onto the core library as soon as possible without confusion.
Cool, would be willing to do that, need a short break from slide preparations :)
:) nice one. Thanks!
There you go: #211