Blocker
say that it shouldn't be passed implicitly (https://github.com/typelevel/cats-effect/blob/master/core/shared/src/main/scala/cats/effect/Blocker.scala#L31). What is the reason behind this recommendation? I used to pass it implicitly and haven't seen any obvious drawbacks so far.
implicit
values should never hold state. The implications of this are pretty profound and far-reaching, and a lot of it comes from experience in violating this rule (and paying the price later). An analogous "best practice" is that you should never do something like implicit def strToInt(s: String): Int = ...
. You certainly can do this, and it'll work sometimes, but you would reject any PR that introduced something like that, wouldn't you? Same thing with implicit
Blocker
.
implicit
Blocker
will bite you. I guess the best thing to look at is the frustrations that people often have around implicit
ExecutionContext
s in the Future
ecosystem.
Blocker
is similar
Pure
/FlatMap
+ the run loop essentially a trampoline?
implicit
values should never hold state."
ContextShift
rather than removing it from Blocker
.
main
however@danielkarch hey,
Aren't Pure/FlatMap + the run loop essentially a trampoline?
well, you need to introduce an async boundary (or more), and then the execution context essentially acts as a trampoline (which is why Future is stack safe, it submits all the time). However having a fully async boundary (i.e. an ExecutionContext submission) is heavyweight, you can build a lighter weight one (this is trampolineEc
in cats-effect).
The thing with stack-safety is that there are some corner cases (you will see comments in the cats-effect codebase like "we need to insert another async boundary here for stack-safety")
Async
, so my async there is not idempotent (which it has to be)
Hello guys!)
I’ve started exploring world of FP recently, and now I’m trying tu build my first http-service with http4s
.
And I get these debug messages in my console (on every request that consul does):
Request headers: Headers(Host: docker.for.mac.localhost:7000, User-Agent: Consul Health Check, Accept: text/plain, text/*, */*, Accept-Encoding: gzip, Connection: close)
[21:31:24.850] [scala-execution-context-global-40 ] DEBUG - closeConnection()
[21:31:24.850] [scala-execution-context-global-40 ] DEBUG - Shutting down HttpPipeline
[21:31:24.850] [scala-execution-context-global-40 ] DEBUG - Canceled request
[21:31:24.850] [scala-execution-context-global-40 ] DEBUG - Shutting down.
[21:31:24.851] [scala-execution-context-global-40 ] DEBUG - Shutting down HttpPipeline
[21:31:24.851] [scala-execution-context-global-40 ] DEBUG - Canceled request
[21:31:24.851] [scala-execution-context-global-40 ] DEBUG - Shutting down.
[21:31:24.851] [scala-execution-context-global-40 ] DEBUG - Shutting down idle timeout stage
[21:31:24.851] [scala-execution-context-global-40 ] DEBUG - Shutting down.
[21:31:24.851] [scala-execution-context-global-40 ] DEBUG - Shutting down.
[21:31:24.851] [blaze-selector-7 ] DEBUG - Stage NIO1HeadStage sending inbound command: Disconnected
[21:31:24.851] [blaze-selector-7 ] DEBUG - Shutting down idle timeout stage
[21:31:24.851] [blaze-selector-7 ] DEBUG - Shutting down.
[21:31:24.851] [blaze-selector-7 ] DEBUG - Stage IdleTimeoutStage sending inbound command: Disconnected
[21:31:24.851] [blaze-selector-7 ] DEBUG - Shutting down HttpPipeline
[21:31:24.851] [blaze-selector-7 ] DEBUG - Canceled request
[21:31:24.851] [blaze-selector-7 ] DEBUG - Shutting down.
My code looks like:
def apply[F[_]: Sync: Async](port: Int)(
implicit cs: ConcurrentEffect[F],
timer: Timer[F],
): F[Http4sPrometheusExporter] = {
for {
prometheusService <- Http4sPrometheusService.build[F]
httpApp = Router[F]("/" -> prometheusService.routes).orNotFound
server <- BlazeServerBuilder[F]
.bindHttp(port, "0.0.0.0")
.withHttpApp(httpApp)
.resource
.use(_ => Async[F].never[Unit])
.start
} yield new Http4sPrometheusExporter()
}
Is it ok? Or I’m duing something wrong?
Appreciate any help :)
Functor
typeclass describes the behavior of map: F[A] => (A => B) => F[B]
and laws like composition; fa.map(f).map(g)
== fa.map(f andThen g)