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 size to Foldable #1114

Merged
merged 1 commit into from
Jun 14, 2016
Merged

Add size to Foldable #1114

merged 1 commit into from
Jun 14, 2016

Conversation

afiore
Copy link
Contributor

@afiore afiore commented Jun 11, 2016

Partially addresses what discussed in #1091.

@afiore
Copy link
Contributor Author

afiore commented Jun 11, 2016

Obviously, this naive implementation would not terminate on infinite streams. @yilinwei was suggesting that a possible approach could be to have size return an Option[Long], which would allow tweaking the implementation for specific instances (e.g. Streams could return None).

@non
Copy link
Contributor

non commented Jun 11, 2016

@afiore I would be fine with that behavior. On the other hand, we already have potentially non-terminating methods (like .toList) so I am fine with this method being unsafe as well (as long as we note that in the documentation).

@ceedubs
Copy link
Contributor

ceedubs commented Jun 11, 2016

I like this approach as opposed to the Option approach. I think that if we want to have support for potentially infinite structures, it should be via a separate method that takes a bound so you can say something like "give me the size, but cap it at 1,000 instead blocking forever on an infinite stream" or something. I think we should keep that out of the scope of this PR though.

I think that this is a great start. There are some additional things that I think should be done:

  • Add a ScalaDoc comment explaining that the default implementation is a naive fold over the structure meaning that 1) it may not terminate and 2) instances for structures that have efficient size implementations should override this
  • Override size in instances for types that have efficient size implementations (Vector comes to mind - are there others?).

Would you be willing to do those? If not, I'm sure that someone else can get them in a followup PR.

@@ -56,6 +57,8 @@ import simulacrum.typeclass
}
}

def size[A](fa: F[A]): Long = foldMap(fa)(_ => 1)
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 I think that Long was a good choice here. Int is probably the first thing that would have come to my mind, but people could conceivably be using Foldable for some very large structures. Though hopefully if their structure is large enough to pass the max Int value they have overridden size to a more efficient implementation :)

@ceedubs
Copy link
Contributor

ceedubs commented Jun 11, 2016

A couple other structures came to mind that could benefit from an override of size: Set, Map, andOneAnd. There may be marginal performance improvements forListandStream`, but it probably doesn't matter much for those.

@codecov-io
Copy link

codecov-io commented Jun 11, 2016

Current coverage is 88.70%

Merging #1114 into master will increase coverage by 0.05%

  1. 2 files (not in diff) in ...main/scala/cats/data were modified. more
  2. 5 files (not in diff) in .../src/main/scala/cats were modified. more
    • Misses +3
    • Hits +7
  3. 2 files in .../src/main/scala/cats were modified. more
    • Misses -4
    • Hits -6
@@             master      #1114   diff @@
==========================================
  Files           227        227          
  Lines          2953       2958     +5   
  Methods        2897       2898     +1   
  Messages          0          0          
  Branches         53         57     +4   
==========================================
+ Hits           2618       2624     +6   
+ Misses          335        334     -1   
  Partials          0          0          

Sunburst

Powered by Codecov. Last updated by b149835...15a0c4d

@afiore
Copy link
Contributor Author

afiore commented Jun 12, 2016

@ceedubs I have added overrides for Map, Set and Vector using the standard library implementation of size, which I believe is defined in scala.collection.TraversableOnce. Not sure I understand how providing an override for OneAnd would help. Wouldn't it just use the instance corresponding to the structure used for the tail (e.g. Foldable[Vector] for OneAnd[Vector, A])?

@ceedubs
Copy link
Contributor

ceedubs commented Jun 12, 2016

Not sure I understand how providing an override for OneAnd would help. Wouldn't it just use the instance corresponding to the structure used for the tail (e.g. Foldable[Vector] for OneAnd[Vector, A])?

@afiore It will use a foldMap derived from the first element plus the foldLeft on the tail structure. If instead there were an override for 1 + size(tail), then it could take advantage of any size optimizations that may be in the Foldable[F] type class for the tail (such as with Vector). Does that make sense?

@afiore afiore force-pushed the implement-size branch 2 times, most recently from 0e462e2 to 3986ec0 Compare June 12, 2016 14:29
@afiore
Copy link
Contributor Author

afiore commented Jun 12, 2016

@ceedubs makes sense! I have amended my commit adding an override for AndOne.

@afiore afiore force-pushed the implement-size branch 2 times, most recently from 4190152 to 2b59ee5 Compare June 12, 2016 20:27
@@ -64,6 +64,12 @@ class OneAndTests extends CatsSuite {
checkAll("NonEmptyList[Int]", ComonadTests[NonEmptyList].comonad[Int, Int, Int])
checkAll("Comonad[NonEmptyList[A]]", SerializableTests.serializable(Comonad[NonEmptyList]))

test("Foldable[OneAnd[Vector, ?]]#size consistent with Vector#size") {
forAll { (oa: OneAnd[Vector, Int]) =>
oa.size should === (1L + oa.tail.size.toLong)
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be duplicating the implementation within the test. Instead, it might be better to do something like oa.size should === (oa.unwrap.size) or oa.size should === (oa.toList.size).

Override with a more performant implementation in Map, Set, Vector and AndOne
using the size method provided by the standard library.

see typelevel#1091
@afiore
Copy link
Contributor Author

afiore commented Jun 14, 2016

@ceedubs I have updated AndOneTests with an assertion that doesn't repeat the actual implementation.

@ceedubs
Copy link
Contributor

ceedubs commented Jun 14, 2016

👍 thanks, @afiore!

@non
Copy link
Contributor

non commented Jun 14, 2016

👍

@non non merged commit 928c78f into typelevel:master Jun 14, 2016
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.

4 participants