Idea: Task
#215
Replies: 4 comments
-
Spitballing some of the conversion functions here: type Task<T, E> = Resolved<T, E> | Rejected<T, E>;
function fromPromise<T, E>(
promise: PromiseLike<T>,
resolved: (t: T) => Resolved<T, E>,
rejected: (err: unknown) => Rejected<T, E>
): Task<T, E> {
let task: Task<T, E>;
promise
.then(value => {
task = resolved(value);
})
.catch(error => {
task = rejected(error);
});
return task;
}
function toPromise<T, E>(task: Task<T, E>): Promise<T> {
return new Promise((resolve, reject) => {
task.match({
Resolved: resolve,
Rejected: reject,
});
});
} Note that this is effectively sugar for |
Beta Was this translation helpful? Give feedback.
-
How about calling it |
Beta Was this translation helpful? Give feedback.
-
@belfz yeah, I used
In terms of which to land on, I intend to spend some time thinking about the semantics of the operation, as I did in choosing the I don't actually particularly love More to come! |
Beta Was this translation helpful? Give feedback.
-
Ha, I see your point! I'm not a huge fan of |
Beta Was this translation helpful? Give feedback.
-
Spitballing here a bit: I've been thinking about a type named
Future
orTask
as a possibly-useful thing to add to the library. This is interesting to me in that it's really useful to have some tools in the space thatPromise
lives in—namely, asynchronous operations and computations—but with different tradeoffs thanPromise
made.For those who might not have thought about what
Promise
does differently from, say,Result
orMaybe
before, there are a bunch of differences, some necessary consequences of how JS works and some less so. The ones that immediately come to mind for me:Promise
combines what True Myth's types callmap
andandThen
into a single function:then
. In terms of type signatures, the way to think about it is thatmap
takes a function whose signature is(t: T) -> U
and gives you back aMaybe<U>
, andandThen
takes a function whose signature is(t: T) -> Maybe<U>
, and gives you back aMaybe<U>
. The same thing goes forResult
.Promise#then
, however, can take either one. That is, if you givethen
a function with the signature(t: T) -> U
, you'll get back aPromise<U>
, and if you givethen
a function with the signature(t: T) -> Promise<U>
you'll also get back aPromise<U>
.This is a choice made for convenience and a sort of accident of history rolled into one, and I'm not at all interested in relitigating it. It is what it is in JavaScript today. More interesting to me is whether we can provide a better interface on top of it.
Promise
to have two types in play—the return type and the type of the error that will show up in thecatch
arm—but unfortunately, the catch argument is (correctly) typed asany
in TypeScript. (Properly, given TS 3.0, we’d probably dounknown
, but TS sensibly tries to avoid backwards-incompatible changes in the standard library types.) This is becausePromise#catch
has to capture any thrown exception along with any rejection created withPromise.reject
.As far as I can see this is a fundamental problem facing any kind of
Task.fromPromise
function, but in principle it’s solveable. You just need a type that takes theany
(or better,unknown
) type handed to thecatch
function and converts it to the desired errorE
type:(error: unknown) => E
.One other note: like
Promise
itself, and unlikeMaybe
andResult
, aTask
captures a kind of computation you can’t (synchronously) exit from! The whole point is to model asynchronous behaviors.Beta Was this translation helpful? Give feedback.
All reactions