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

Sync NEL and NEV methods (fixes #1832) #1838

Merged
merged 6 commits into from
Oct 17, 2017
Merged

Sync NEL and NEV methods (fixes #1832) #1838

merged 6 commits into from
Oct 17, 2017

Conversation

durban
Copy link
Contributor

@durban durban commented Aug 21, 2017

There is one notable change: NEL previously had a concat which accepted another NEL. To be similar to NEV, I changed this (and added concatNel).

The following differences are (intentionally) not addressed:

  • groupBy: NEL has one, but I didn't add it to NEV, because I think that it should have an Eq constraint, however implementing that would need a Map which works with Eq...
  • length/size: NEV has a length: Int and a size: Long (as an extension method). NEL had a size: Int and I've added length: Int. (Possibly size should be changed to Long, or simply removed, to be an extension method.)
  • a few Vector-specific methods, e.g., updated.

This fixes #1832 (cc @johnynek).

@codecov-io
Copy link

codecov-io commented Aug 21, 2017

Codecov Report

Merging #1838 into master will increase coverage by 0.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #1838      +/-   ##
=========================================
+ Coverage   96.09%   96.1%   +0.01%     
=========================================
  Files         273     273              
  Lines        4554    4566      +12     
  Branches      129     122       -7     
=========================================
+ Hits         4376    4388      +12     
  Misses        178     178
Impacted Files Coverage Δ
core/src/main/scala/cats/data/NonEmptyVector.scala 100% <100%> (ø) ⬆️
core/src/main/scala/cats/data/NonEmptyList.scala 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d7a0204...f18b06f. Read the comment docs.

Copy link
Contributor

@johnynek johnynek left a comment

Choose a reason for hiding this comment

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

Thanks for sending the PR to fix this!

/**
* Append another NonEmptyList
*/
def concatNel[AA >: A](other: NonEmptyList[AA]): NonEmptyList[AA] =
Copy link
Contributor

Choose a reason for hiding this comment

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

why can't this just be concat? NonEmptyList and List are different types. I'm not sure why we took this route and if there is not a good reason I'd rather change it before 1.0.

@@ -139,12 +153,6 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) {
}

/**
* Append another NonEmptyList
*/
def concat[AA >: A](other: NonEmptyList[AA]): NonEmptyList[AA] =
Copy link
Contributor

Choose a reason for hiding this comment

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

I like this naming better. We should add concat to NonEmptyVector in my opinion, not remove here.

@durban
Copy link
Contributor Author

durban commented Aug 21, 2017

@johnynek I guess the concat/concatNel/concatNev is to avoid overloading. I don't feel very strong about it, I just followed NEV. I could live with it either way. (However, I think there are people who really don't like overloading, e.g., there is a wart in WartRemover, which forbids it. Maybe we should ask them...)

def reverse: NonEmptyVector[A] =
new NonEmptyVector(toVector.reverse)

def zipWithIndex: NonEmptyVector[(A, Int)] =
Copy link
Collaborator

Choose a reason for hiding this comment

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

You could use this to override Traverse#zipWithIndex for NonEmptyVector.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@durban
Copy link
Contributor Author

durban commented Aug 23, 2017

@johnynek Here is the issue and PR which removed concat overloading from NEV. This PR also has some discussion about overloading in general.

@kailuowang kailuowang added this to the 1.0.0-RC1 milestone Aug 28, 2017
@@ -237,6 +255,9 @@ private[data] sealed trait NonEmptyVectorInstances {
override def traverse[G[_], A, B](fa: NonEmptyVector[A])(f: (A) => G[B])(implicit G: Applicative[G]): G[NonEmptyVector[B]] =
G.map2Eval(f(fa.head), Always(Traverse[Vector].traverse(fa.tail)(f)))(NonEmptyVector(_, _)).value

override def zipWithIndex[A](fa: NonEmptyVector[A]): NonEmptyVector[(A, Int)] =
fa.zipWithIndex
Copy link
Contributor

Choose a reason for hiding this comment

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

this is untested, so I guess that means zipWithIndex is currently lawless.

Copy link
Member

Choose a reason for hiding this comment

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

We could write a test to check if zipWithIndex is consistent with zipWith(Range(0, size)) :)

@LukaJCB
Copy link
Member

LukaJCB commented Sep 15, 2017

Hey @durban, are you still working on this? Or would you like some help? :)

@durban
Copy link
Contributor Author

durban commented Sep 16, 2017

@LukaJCB I'm not sure what's left to do (if any) ...

@LukaJCB
Copy link
Member

LukaJCB commented Sep 16, 2017

I agree there's not much left :)
However could you address the review comments? Some of the extra code isn't tested, you can check this in the codecov link above!
Also, you currently have some conflicts, so a rebase would be great :)

@durban
Copy link
Contributor Author

durban commented Sep 16, 2017

I've rebased and also added more tests for zipWithIndex. Please let me know if I've missed anything else.

@LukaJCB
Copy link
Member

LukaJCB commented Sep 16, 2017

LGTM (pending concensus on concat) thank you very much! :)

@kailuowang
Copy link
Contributor

kailuowang commented Sep 21, 2017

👍 I am fine with theconcat as in this PR. We should probably not go back and forth on overrides. @johnynek WDYT?

@LukaJCB
Copy link
Member

LukaJCB commented Sep 28, 2017

I've thought a bit more about this and I think having concat concatenate a NEL and a List is fine for the most part, mainly because most concatenation will probably done using the Semigroup instance and |+| is as short as it gets.
However, I disagree on keeping ++ as to me, it implies symmetry between the two types. I'd expect something with the operator ++ to take two arguements of the same type on either side, so I think we should just get rid of ++. People can use concat instead.

I'd also be fine with having concat be the concatenation between two same types and using concatList and concatVector accordingly.

@kailuowang
Copy link
Contributor

@durban do you still have time to work on this? if you want, I am happy to continue it for you.

@durban
Copy link
Contributor Author

durban commented Oct 4, 2017

I'd argue that ++ only accepting a NEL/NEV would make it less useful. ++ as it is, has a less "restrictive" signature (it's possible to make a List/Vector from a NEL/NEV; the other direction is not possible). (Please also note, that I absolutely did not touch ++ in this PR.)

@kailuowang
Copy link
Contributor

kailuowang commented Oct 5, 2017

Here is my final proposal, it looks that we have to make some trade off or we are going to have to break either NEL or NEV. The trade off I am willing to make here is to allow overload in this particular case.

So in NEL we are going to have

concat(l: List) //new
concat(nel: NEL)
concatNel(nel: NEL) //new
++(l: List)

the concat(nel: NEL) is the overload to allow backward comp.
in NEV we are going to have

concat(v: Vector)
concat(nev: NEV)  //new
concatNev(nev: NEV)
++(l: Vector)

the concat(nev: NEV) is the overload to maintain symmetry.

In PR #1949
we will add to NEL

def :::(other: NEL)

and add to NEV

def ++:(other: NEV)

This might mitigate the @LukaJCB 's concern over an asymmetric symbol concatenation.
WDYT? @johnynek @durban @LukaJCB

@jcranky
Copy link
Contributor

jcranky commented Oct 5, 2017

For NEV, you mean concat(v: Vector), not concat(l: List), right?

@LukaJCB
Copy link
Member

LukaJCB commented Oct 5, 2017

I think that's a good idea 👍

Edit: Actually I'm not sure right now, the way you put it, it has the symmetric operators for different types and the ++: asymmetric operator for two same types, shouldn't we switch that around? :)
And on another note, do we really need another operator for List concatenation?
Through semigroup and SemigroupK we already have |+| and <+>, so I don't necessarily think we need a third one. That said, I'd be fine with that to avoid breakage

@jcranky jcranky mentioned this pull request Oct 5, 2017
@durban
Copy link
Contributor Author

durban commented Oct 5, 2017

@kailuowang I'm ok with your proposal. If others also agree, I can add it to this PR.

@kailuowang
Copy link
Contributor

@LukaJCB the idea is to avoid unnecessary breaking changes. ++: and ::: is kind of inherited from Vector and List. We keep ++ the same to maintain backward compat. I can see arguments on both side for ++ to take a NEL or List. So I am not convinced that we need a breaking change to chose one over the other. If we want ++ to take NEL and NEV I incline to add an override in another PR.

@LukaJCB
Copy link
Member

LukaJCB commented Oct 5, 2017

I'm good with asymmetry when it means as little breaking changes as possible 👍

@durban
Copy link
Contributor Author

durban commented Oct 5, 2017

@kailuowang This is not gonna work:

[error] def concat[AA >: A](other: Vector[AA]): cats.data.NonEmptyVector[AA] at line 77 and
[error] def concat[AA >: A](other: cats.data.NonEmptyVector[AA]): cats.data.NonEmptyVector[AA] at line 79
[error] have same type after erasure: (other: scala.collection.immutable.Vector)scala.collection.immutable.Vector

(For NEL it would work, because that isn't a value class, but at that point we'd lose the consistency.)

There are various ugly workarounds for this, but I think they're more ugly than only having concatNel/concatNev.

@ceedubs
Copy link
Contributor

ceedubs commented Oct 5, 2017

In previous PRs we've discussed overloading and the general consensus seemed to be that the downsides outweigh the benefits. Personally I'm 👍 for concatNel and concatNev.

@kailuowang
Copy link
Contributor

Okay, can we add concat(nel: NEL) back as a deprecated overload.
We will need a scalafix migration. concat(nel) -> concatNel(nel), doesn't need to be this PR though, unless if you are interested in trying out the scalafix stuff.

@durban
Copy link
Contributor Author

durban commented Oct 6, 2017

@kailuowang Done. I'd skip learning scalafix for now.

@@ -12,6 +12,7 @@ import cats.data.{NonEmptyList, NonEmptyVector}
import cats.laws.discipline.arbitrary._
import cats.laws.discipline.{ComonadTests, NonEmptyTraverseTests, MonadTests, ReducibleTests, SemigroupKTests, SerializableTests}

@deprecated("to be able to test deprecated methods", since = "1.0.0-RC1")
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry I hate to drag on this but I wouldn't deprecate the whole suite for one test. I'd rather we duplicated the test suite class with just the concat test in it and deprecate that one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

kailuowang
kailuowang previously approved these changes Oct 11, 2017
LukaJCB
LukaJCB previously approved these changes Oct 14, 2017
Copy link
Member

@LukaJCB LukaJCB left a comment

Choose a reason for hiding this comment

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

Thanks for this! :)

@kailuowang kailuowang mentioned this pull request Oct 16, 2017
12 tasks
@LukaJCB
Copy link
Member

LukaJCB commented Oct 16, 2017

Hey @durban, sorry to bother you again, but we just need to fix those conflicts, and then we're good to merge! :) If you're too busy, or don't want to invest more time, that's completely okay!

@durban durban dismissed stale reviews from LukaJCB and kailuowang via f18b06f October 17, 2017 19:06
Copy link
Member

@LukaJCB LukaJCB left a comment

Choose a reason for hiding this comment

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

LGTM, thanks a lot! :) Will merge when Travis is done

@kailuowang kailuowang merged commit 8d94237 into typelevel:master Oct 17, 2017
@kailuowang
Copy link
Contributor

haha I beat you @LukaJCB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

NonEmptyVector.last
8 participants