-
Notifications
You must be signed in to change notification settings - Fork 789
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
Small refactor to DefaultClient to remove unsafe calls and repeated code #7417
Small refactor to DefaultClient to remove unsafe calls and repeated code #7417
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it. Thanks!
@@ -88,14 +87,13 @@ private[http4s] abstract class DefaultClient[F[_]](implicit F: MonadCancelThrow[ | |||
def streaming[A](req: F[Request[F]])(f: Response[F] => Stream[F, A]): Stream[F, A] = | |||
Stream.eval(req).flatMap(stream).flatMap(f) | |||
|
|||
private def reqWithMediaRangeAndQValue[A](req: Request[F], d: EntityDecoder[F, A]) = | |||
d.consumes.toList.toNel.fold(req)(m => req.addHeader(Accept(m.map(MediaRangeAndQValue(_))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably a few femtoseconds slower than the original, but since we're about to make an HTTP request, it is probably completely imperceptible. And I like getting rid of the unsafe methods. And if someone wants to reoptimize this, now it's in one place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.toList.toNel
In the capacity of a grumpy micro-benchmarker of certain parts of http4s, this will also consume a bit more heap and stress out the GC (in terms of femtoseconds). Unsafe API here is a reasonable choice since we have checks on non-emptiness.
But, I really like this refactoring into the dedicated def
👍🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I like getting rid of the unsafe methods. And if someone wants to reoptimize this, now it's in one place.
Unsafe API here is a reasonable choice since we have checks on non-emptiness.
I'm happy to rework to go with whatever maintainer preference is here. I think the .toNel.fold
implementation nicer and more pleasing, but if the performance decrease is deemed important I could push up changes to have the old implementation pulled out into the same helper function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which part is it that's expected to add a bit more heap / GC / execution time? Would this be an improvement?
private def reqWithMediaRangeAndQValue[A](req: Request[F], d: EntityDecoder[F, A]) =
d.consumes.toList match {
case head :: next =>
req.addHeader(Accept(NonEmptyList(head, next).map(MediaRangeAndQValue(_))))
case Nil => req
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which part is it that's expected to add a bit more heap / GC / execution time? Would this be an improvement?
private def reqWithMediaRangeAndQValue[A](req: Request[F], d: EntityDecoder[F, A]) = d.consumes.toList match { case head :: next => req.addHeader(Accept(NonEmptyList(head, next).map(MediaRangeAndQValue(_)))) case Nil => req }
I think this would be an improvement. It avoids allocating the extra Option
that using toNel
had.
The original implementations avoiding calling toList
if the Set
was empty, but doing that is just going to return Nil
if the set is empty anyways. It's fine.
@Adam-McDevitt, if you're still willing, yes I think your suggestion here would be an improvement.
Since Ross approved this, it should be enough to move on. I don't really want to be a party pooper. But maybe some third folk will glance at this to have a quorum in opinions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this @Adam-McDevitt, it's a nice clean up.
I do think your latest suggestion avoiding toNel
is nice and hope you are able to push a commit with that change. 😺
Approving to not block.
@@ -88,14 +87,13 @@ private[http4s] abstract class DefaultClient[F[_]](implicit F: MonadCancelThrow[ | |||
def streaming[A](req: F[Request[F]])(f: Response[F] => Stream[F, A]): Stream[F, A] = | |||
Stream.eval(req).flatMap(stream).flatMap(f) | |||
|
|||
private def reqWithMediaRangeAndQValue[A](req: Request[F], d: EntityDecoder[F, A]) = | |||
d.consumes.toList.toNel.fold(req)(m => req.addHeader(Accept(m.map(MediaRangeAndQValue(_))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which part is it that's expected to add a bit more heap / GC / execution time? Would this be an improvement?
private def reqWithMediaRangeAndQValue[A](req: Request[F], d: EntityDecoder[F, A]) = d.consumes.toList match { case head :: next => req.addHeader(Accept(NonEmptyList(head, next).map(MediaRangeAndQValue(_)))) case Nil => req }
I think this would be an improvement. It avoids allocating the extra Option
that using toNel
had.
The original implementations avoiding calling toList
if the Set
was empty, but doing that is just going to return Nil
if the set is empty anyways. It's fine.
@Adam-McDevitt, if you're still willing, yes I think your suggestion here would be an improvement.
Thanks all - I'll push up a change probably today with my latest suggestion. |
Updated! :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs a sbt scalafixAll
Thank you for iterating over this! |
Saw some unsafe calls:
NonEmptyList.fromListUnsafe
m.head
wherem
is aList
, not aNonEmptyList
that could be easily refactored to a safe call, and an opportunity to move some repeated code into a private function.