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

Add Order/Hash/Show Instances For Uri And Related Types #3969

Merged
merged 3 commits into from Dec 2, 2020

Conversation

isomarcte
Copy link
Member

@isomarcte isomarcte commented Nov 29, 2020

This commit adds Order, Hash, and Show instances for the following types.

  • org.http4s.Uri.Authority
  • org.http4s.Query
  • org.http4s.Uri.Host
  • org.http4s.Uri.RegName
  • org.http4s.Uri

The impetus for all of them was to support instances for org.http4s.Uri.

In addition, two functions were added to the package object org.http4s.internal scoped to be private to http4s, compareField, reduceComparisons_, and reduceComparisons.

These functions substantially reduce the amount of code required to write Order instances for Product types.

In order to support testing the laws for these types, several new instances of Arbitrary and Cogen were added, as not all types had instances for each of these.

@isomarcte
Copy link
Member Author

@rossabaker so this commit breaks binary compatibility in two ways.

For org.http4s.Uri#Authority, the addition of a companion object messes up the type hierarchy from the one generated by the case class.

Error:  core: Failed binary compatibility check against org.http4s:http4s-core_2.13:0.21.1! Found 1 potential problems
Error:   * the type hierarchy of object org.http4s.Uri#Authority is different in current version. Missing types {scala.runtime.AbstractFunction3}
Error:     filter with: ProblemFilters.exclude[MissingTypesProblem]("org.http4s.Uri$Authority$")

It's simple enough to add that back in, though a bit gross.

  object Authority extends scala.runtime.AbstractFunction3[Option[UserInfo], Host, Option[Int], Authority] {

    override def apply(userInfo: Option[UserInfo] = None, host: Host = RegName("localhost"), port: Option[Int] = None): Authority =
      new Authority(userInfo, host, port)

The binary change is the addition of the new Cogen and Arbitrary instances. This is an additive change, so no symbols are changed/removed.

My intuition would be to add the AbstractFunction3 extension now, then remove it on the rebase to the main branch. For the new instances for Cogen and Arbitrary, I'd just add them to the ProblemFilters for mima on this branch, since they are just additions, not breaking changes.

What are your thoughts?

@rossabaker
Copy link
Member

I did exactly that with an AbstractFunction1 somewhere else. I think your idea is exactly right.

The arbitrary changes pass MiMa? Adding to traits was bad in 2.11, but I think is fine starting in 2.12 as long as there is a concrete implementation.

@isomarcte
Copy link
Member Author

@rossabaker I've updated the PR to include the AbstractFunction3 workaround.

The arbitrary changes pass MiMa? Adding to traits was bad in 2.11, but I think is fine starting in 2.12 as long as there is a concrete implementation.

Yeah, that is my understanding as well. To be clear, MiMa does fail on them, but I don't think they are actually an issue unless http4s is still targeting 2.11.x.

Thoughts?

@rossabaker
Copy link
Member

I usually end up regretting making MiMa exceptions. I think I understand, and then a week later, we get a bug.

We already have a couple in the companion object. I think making them a trait was a mistake, and most people should be importing them. I'd be fine putting them in the companion, and we can reunify them somewhere in 1.0.

@isomarcte
Copy link
Member Author

@rossabaker sounds good. I've updated this with the new instances only in the companion object.

@isomarcte isomarcte force-pushed the uri-order branch 2 times, most recently from 4e7c6bf to 78427b4 Compare November 30, 2020 18:42
@isomarcte
Copy link
Member Author

@rossabaker I had to make a few things lazy to deal with some initialization errors coming from the interplay between the static scope of the companion object and the trait, just to let you know.

@isomarcte
Copy link
Member Author

@rossabaker as per other PRs, the failures here do not appear related to my change.

Copy link
Member

@rossabaker rossabaker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree the test failures are unrelated. I did come up with a few more ideas.

@@ -82,4 +84,54 @@ package object http4s {
type Http4sSyntax = syntax.AllSyntax
@deprecated("Moved to org.http4s.syntax.all", "0.16")
val Http4sSyntax = syntax.all

// Helper functions for writing Order instances //
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We keep a lot of helpers in org.http4s.internals, just to reduce clutter.

* and `SemigroupK[Option].combineK` have different semantics. We
* ''always'' want the `SemigroupK[Option]` instance here.
*/
private[http4s] def reduceComparisons(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runs all the comparisons, right? Could we do a lazy one that only continues for as long as we get zeroes?

@@ -634,11 +638,12 @@ private[http4s] trait ArbitraryInstances {
} yield Uri.Authority(maybeUserInfo, host, maybePort)
}

val genPctEncoded: Gen[String] =
lazy val genPctEncoded: Gen[String] =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A long, long time ago, I saw lazy arbitraries making a difference in a profile, which caused us to use eager vals. I don't know whether that's still a concern, but I think you could just by moving them above the new use.

Whether eager values are smart in a trait is a separate question.

I'm fine either way.

@isomarcte
Copy link
Member Author

@rossabaker Updated.

I moved the helpers into the org.http4s.internal package object, made reduceComparisons lazy for every comparison other than the first, and shuffled things around in ArbitraryInstances so that it could use strict values again.

This commit adds `Order`, `Hash`, and `Show` instances for the following types.

* `org.http4s.Uri.Authority`
* `org.http4s.Query`
* `org.http4s.Uri.Host`
* `org.http4s.Uri.RegName`
* `org.http4s.Uri`

The impetus for all of them was to support instances for `org.http4s.Uri`.

In addition, two functions were added to the package object `org.http4s.internal` scoped to be private to `http4s`, `compareField`, `reduceComparisons_`, and `reduceComparisons`.

These functions substantially reduce the amount of code required to write `Order` instances for Product types.

In order to support testing the laws for these types, several new instances of `Arbitrary` and `Cogen` were added, as not all types had instances for each of these.
Copy link
Member

@rossabaker rossabaker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Thanks!

@isomarcte
Copy link
Member Author

@rossabaker I saw this was missing an import after the merge with for the implicits <-> syntax change. I've updated the branch.

@rossabaker
Copy link
Member

I gambled and merged in the browser last night without compiling. Thanks for the mop-up!

@rossabaker rossabaker merged commit 929ef0b into http4s:series/0.21 Dec 2, 2020
@isomarcte isomarcte deleted the uri-order branch December 2, 2020 21:03
@isomarcte isomarcte mentioned this pull request Dec 10, 2020
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 this pull request may close these issues.

None yet

2 participants