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

Why not Dispatcher? #334

Closed
mcanlas opened this issue Mar 25, 2023 · 4 comments · Fixed by #335
Closed

Why not Dispatcher? #334

mcanlas opened this issue Mar 25, 2023 · 4 comments · Fixed by #335

Comments

@mcanlas
Copy link
Contributor

mcanlas commented Mar 25, 2023

Hello from Scalar 2023 in Warsaw! I saw a presentation by Kamil Kloch about cats-effect and learned about Dispatcher being the go-to mechanism for running IO's in unsafe territory.

I'm in a position where I've home-rolled a solution that is similar to Feral (build a resource, manually initialize, toss away the finalizer bc lifetimes are hard) and in the last mile call unsafeRunSync.

The presentation I watched was advocating that there shouldn't be a reason to do that and just use a Dispatcher .use() instead "because reasons". Something about the Dispatcher having a better handle on initializing what is required to run IOs?

  • A) How is the Dispatcher's run different than just using the unsafe global provided?
  • B) Should feral replace its unsafeRunSync with a Dispatcher per the advice above? Is there some nuance/extra dimension we are missing?

Edit: I believe Dispatcher thoughts are a continuation on #33

@armanbilge
Copy link
Member

armanbilge commented Mar 27, 2023

Thanks for opening the issue! It's a good question, and I think we can revisit this design choice.

How is the Dispatcher's run different than just using the unsafe global provided?

Here are three advantages of using Dispatcher#unsafeRun over IO#unsafeRun*:

  1. You can have a dispatcher in a generic effect F[_] i.e. Dispatcher[F]
  2. A dispatcher has a scope / lifecycle i.e. it is a Resource[F, Dispatcher[F]]. You can be confident that tasks are not being leaked after the dispatcher has been closed.
  3. A Dispatcher is backed by an already running "warm" fiber. Meanwhile, IO#unsafeRun* has to do extra set-up every time it is used, e.g. registering fatal error handlers.

Should feral replace its unsafeRunSync with a Dispatcher per the advice above?

Yes and no :) point (3) above is a good motivation to create a dispatcher when the lambda starts and use it to handle all incoming requests.

The question is, how will we get that dispatcher? Since the only way to create a Dispatcher is inside of an effectful resource, but without the dispatcher we can't run any effects.

So, we will still need IO#unsafeRun*. We can use this exactly once to obtain a dispatcher during the setup process, that we can then use for all subsequent requests.

Does that answer your question? Thanks, and PRs welcome for this :)

@mcanlas
Copy link
Contributor Author

mcanlas commented Mar 27, 2023

So are you suggesting that with both Feral and my work, we should bootstrap a Dispatcher like a resource for the lifetime of the lambda, and then continually use that Dispatcher for the times when we need to be unsafe?

@armanbilge
Copy link
Member

Yes, exactly. It seems to me that an application should never have to call IO.unsafeRun* more than once: instead, it can use that one call to setup a Dispatcher, and use that for all subsequent unsafe calls.

@mcanlas
Copy link
Contributor Author

mcanlas commented Mar 30, 2023

Shout out @kamilkloch :)

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

Successfully merging a pull request may close this issue.

2 participants