Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array.IO.sequence vs Promise.all/Promise.any #297

Open
BlueHotDog opened this issue May 13, 2021 · 6 comments
Open

Array.IO.sequence vs Promise.all/Promise.any #297

BlueHotDog opened this issue May 13, 2021 · 6 comments

Comments

@BlueHotDog
Copy link

Hey, trying to understand how Array.IO.sequence is compared to Promise.all/Promise.any.
Are the IOs running on parallel?
Is it like Promise.all that if one fails all fails? (looks like, since it doesnt look like it collects the errors)
Maybe worth having an Promise.any solution too?

@AlexaDeWit
Copy link

It runs them in parallel at present. I'd have to check, but I believe it preserves order, and emits the first error occurred (based on the order of the array, not of resolution), not all errors.

@andywhite37
Copy link
Member

Yeah, I think @AlexaDeWit is right - if I recall it runs the async work in parallel (because the default Applicative for IO is setup to run the tasks in parallel), and it has fail-fast semantics, where it bails as soon as there is a single error and returns that error. It doesn't have error-collecting semantics like something like Validation, but that would be nice to have.

I don't think we have an any kind of function right now, but that would also be nice to have - this is sometimes called race. If anyone wants to PR that change, that would be cool. I haven't had a lot of time to dedicate to relude recently, but I can try to add it at some point.

@johnhaley81
Copy link
Contributor

I just talked to @BlueHotDog a bit in discord and wanted to bring a few things into this thread:

  • IO.all is most definitely parallel, we confirmed this
  • To get any like behavior, you can do:
module IOWithVoidError =
  IO.WithError({
    type t = Void.t;
  });

let foo = iosToRun => 
  iosToRun
  |> List.map(IO.summonError)
  |> IOWithVoidError.all
  |> IO.map(res => Js.log2("done", res))
  |> IO.mapError(ignore);

The key here using IO.summonError and making the IOWithVoidError module

@BlueHotDog
Copy link
Author

thanks @johnhaley81 ! was just about to write that :)

Since i'm using rescript, the code i ended up with is:

module IOWithVoidError =
  IO.WithError({
    type t = Void.t;
  });

let collect = array => {
  array
  |> List.fromArray
  |> List.map(IO.summonError)
  |> IOWithVoidError.all
  |> IO.map(res => res |> Relude.Array.fromList)
  |> IO.mapError(ignore);
};

It would be cool to have allArray or something to avoid the array->list->array conversion

@andywhite37
Copy link
Member

While we're talking about IO, I would just put it out there that the implementation is probably not the best. IO is not stack-safe and is over-complicated in terms of all the constructors. I think if I could go back, I probably would have greatly simplified it. I like the current interface of IO, but the underlying implementation could probably be reworked a bit, if that would help with the addition of new features.

Making it stack-safe would be a really nice-to-have. It might also make sense to just create a new "async effect type" off to the side of IO (in relude or in a separate library), prove it out, and then eventually replace IO with this new type. That would be a bigger effort if anyone is interested.

Desired new features:

  • stack safety
  • maybe the option of having different Applicative/Monad instances (one for fail-fast and one for parallel/error-collecting)
  • simplification of the constructors
  • other things?

@AlexaDeWit
Copy link

Yeah, I think @AlexaDeWit is right - if I recall it runs the async work in parallel (because the default Applicative for IO is setup to run the tasks in parallel), and it has fail-fast semantics, where it bails as soon as there is a single error and returns that error. It doesn't have error-collecting semantics like something like Validation, but that would be nice to have.

I don't think we have an any kind of function right now, but that would also be nice to have - this is sometimes called race. If anyone wants to PR that change, that would be cool. I haven't had a lot of time to dedicate to relude recently, but I can try to add it at some point.

If you're happy with what I'm suggesting in #298 I'd be happy to do both simultaniously?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants