hbr / fmlib

Functional Monadic Library for Ocaml
BSD 3-Clause "New" or "Revised" License
45 stars 3 forks source link

Feature request: parallel tasks #7

Open benjamin-thomas opened 9 months ago

benjamin-thomas commented 9 months ago

Hello Helmut,

I'm wondering if you're planning on adding some kind of parallelization for tasks. For comparaison, and to my knowledge, this is not possible currently by default in Elm.

I mimicked Elm's API within my code, like such:

(** sequence [ Task.return 1; Task.return 2 ] = Task.return [1; 2] *)
let sequence lst =
  let ( let* ) = Task.( >>= ) in
  List.fold_left
    (fun acc item ->
      let* acc = acc in
      let* item = item in
      Task.return (item :: acc))
    (Task.return [])
    lst
;;

To then call a function like such:

let fetch_users_with_posts users = sequence (List.map fetch_user_with_posts users)

However, as you can see in the following screen shot, the requests happen in sequence (as expected).

image

Ideally, I'd like to run these requests in parallel so that I wouldn't get a "cascading effect".

You can find my example code here if it's of any use: https://github.com/benjamin-thomas/tea-playground/blob/5bbcdf78ce7caa056fbb6b1e12e559b286b33362/ocaml/fmlib/combine-http-requests/main.ml#L98

Thanks

hbr commented 9 months ago

Hello Benjamin,

the monadic bind operator (>>=) and let* are inevitably sequence operators. If you use task >>= f the function f cannot be called before the result of task is available.

As opposed to tasks, commands are not sequenced. If you perform several tasks each in its own command (batching the commands into one command), then the tasks should run in parallel. Since you have already a test environment could you do me the favour and verify this.

However in your specific example I see an inherent need to first execute one task which gets the users and then run the tasks to get the posts of each individual user in parallel. This makes the logic cleaner than to run first a command getting the users and start via update the commands which get the posts of all users in parallel.

I have made a draft implementation to execute parallel task execution in the branch browser of fmlib. You can view the interface in https://github.com/hbr/fmlib/blob/d7cfa51c9dab6f8269dffec5a49bdd228e22c6f2/src/browser/fmlib_browser.mli#L986C1-L999.

If you like it, you can try it (e.g. via opam pin) a give me feedback.

Regards Helmut

benjamin-thomas commented 9 months ago

Hello Helmut,

Regarding the bind operation it makes sens!

I can indeed run those requests in parallel via Command.batch. Example here.

I was exploring the idea of chaining computation to avoid putting too much logic in the update function.

I will play with the parallel function and we'll let you know how it goes.

hbr commented 9 months ago

Thanks for the feedback. It is good to here that batching commands runs the commands in parallel. It should do so by design, but I have never verified it. Thanks for checking it.

I hope the parallel function does the same at the task level.

hbr commented 7 months ago

Hello Benjamin,

are you happy with the function parallel? It is still in the branch browser and not yet merged into the main branch and released.

benjamin-thomas commented 7 months ago

Hi Helmut!

Sorry for getting back so late.

I tested the function here: https://github.com/benjamin-thomas/tea-playground/blob/4603eca67b6789b97d27662f3315359b5d59f1f5/ocaml/fmlib/combine-http-requests-parallel2/main.ml#L56

I think it's looking good. It's an interesting option because it frees us from being force to handle the accumulating logic in the update function.

Here's my example in action

Peek 03-02-2024 20-54

Would you be interested in receiving small examples such as these to augment the documentation? A user had question a few days ago on the OCaml forum so I'm thinking maybe I could add a few examples.

hbr commented 7 months ago

Thanks for the feedback. The function parallel seems to work as expected. The version 0.5.8 is currently in the release process in the opam repository. It mainly contains unicode parsing functions. But the function parallel is contained as well.

After the successful release you can cancel the pin and use the new version.

To your question: It is good to improve the documentation with small self contained examples.