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

remove iteratorFoldM fixes #1716 #1740

Merged
merged 8 commits into from
Jun 26, 2017

Conversation

kailuowang
Copy link
Contributor

@@ -214,17 +209,23 @@ class FoldableTestsAdditional extends CatsSuite {
}

test(".foldLeftM short-circuiting optimality") {
// test that no more elements are evaluated than absolutely necessary
// test that no more than 1 elements are evaluated than absolutely necessary
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we look if we can solve this additional evaluation with @TomasMikula's suggestion?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated.

Copy link
Collaborator

@peterneyens peterneyens Jun 23, 2017

Choose a reason for hiding this comment

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

You forgot to update this comment. Fixed it already 👍

@codecov-io
Copy link

codecov-io commented Jun 23, 2017

Codecov Report

Merging #1740 into master will decrease coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1740      +/-   ##
==========================================
- Coverage   93.99%   93.99%   -0.01%     
==========================================
  Files         253      253              
  Lines        4180     4179       -1     
  Branches      155       85      -70     
==========================================
- Hits         3929     3928       -1     
  Misses        251      251
Impacted Files Coverage Δ
core/src/main/scala/cats/data/NonEmptyVector.scala 100% <ø> (ø) ⬆️
core/src/main/scala/cats/instances/map.scala 94.44% <ø> (-0.16%) ⬇️
core/src/main/scala/cats/data/NonEmptyList.scala 97.36% <ø> (-0.03%) ⬇️
core/src/main/scala/cats/instances/set.scala 100% <ø> (ø) ⬆️
core/src/main/scala/cats/instances/list.scala 100% <100%> (ø) ⬆️
core/src/main/scala/cats/Foldable.scala 98.7% <100%> (-0.05%) ⬇️
core/src/main/scala/cats/instances/stream.scala 94.64% <100%> (+0.41%) ⬆️
core/src/main/scala/cats/instances/vector.scala 97.77% <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 e3a969e...312ee35. Read the comment docs.

@kailuowang
Copy link
Contributor Author

weird, for some reason github doesn't allow me to request @TomasMikula to review.

@kailuowang kailuowang added this to the 1.0.0-MF milestone Jun 23, 2017
@TomasMikula
Copy link
Contributor

👍

However, it's still the case that most data structures can have a more efficient implementation of foldM than the default one from Foldable.

@kailuowang
Copy link
Contributor Author

@TomasMikula yes, it's nice that you also mentioned it in the comment of foldM. we'll get to it in a later PR(s).

@@ -90,9 +90,6 @@ trait ListInstances extends cats.kernel.instances.ListInstances {

override def filter[A](fa: List[A])(f: A => Boolean): List[A] = fa.filter(f)

override def foldM[G[_], A, B](fa: List[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] =
Foldable.iteratorFoldM(fa.toIterator, z)(f)
Copy link
Contributor

Choose a reason for hiding this comment

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

can't we maybe do this one?

def step(in: (List[A], B])): G[Either[(List[A], B), A]] = in match {
  case (Nil, b) => G.pure(Right(b))
  case (a :: tail, b) => G.map(f(a, b)) { bnext => Left((tail, bnext)) }
} 

G.tailRecM((fa, z))(step)

right? If we had this, we could cover the common case for List and NonEmptyList, no?

@@ -118,9 +118,6 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {

override def collect[A, B](fa: Stream[A])(f: PartialFunction[A, B]): Stream[B] = fa.collect(f)

override def foldM[G[_], A, B](fa: Stream[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] =
Foldable.iteratorFoldM(fa.toIterator, z)(f)
Copy link
Contributor

Choose a reason for hiding this comment

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

we could copy the List approach above here:

def step(in: (Stream[A], B])): G[Either[(Stream[A], B), A]] = in match {
  case (Stream.empty, b) => G.pure(Right(b))
  case (a #:: tail, b) => G.map(f(a, b)) { bnext => Left((tail, bnext)) }
} 

G.tailRecM((fa, z))(step)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Patten match Stream into a #:: tail will force an evaluation of the head of the tail, so I used vanilla if

@@ -90,9 +90,6 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances {

override def collect[A, B](fa: Vector[A])(f: PartialFunction[A, B]): Vector[B] = fa.collect(f)

override def foldM[G[_], A, B](fa: Vector[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] =
Foldable.iteratorFoldM(fa.toIterator, z)(f)
Copy link
Contributor

Choose a reason for hiding this comment

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

Vector.tail is so slow, we are probably better off doing fa.toList.foldM here if we do the above.

@kailuowang
Copy link
Contributor Author

kailuowang commented Jun 23, 2017

@johnynek thanks for helping me with these, it was too lazy of me. I've updated the implemenations.

@@ -118,8 +119,17 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances {

override def collect[A, B](fa: Stream[A])(f: PartialFunction[A, B]): Stream[B] = fa.collect(f)

override def foldM[G[_], A, B](fa: Stream[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] =
Foldable.iteratorFoldM(fa.toIterator, z)(f)
override def foldM[G[_], A, B](fa: Stream[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = {
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 good, but now again the default implementation of foldM in Foldable is not tested.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops. I'll correct the test.

Copy link
Contributor Author

@kailuowang kailuowang Jun 23, 2017

Choose a reason for hiding this comment

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

updated. I also added the original redundant tests back to test the stream override implementation.

Copy link
Collaborator

@peterneyens peterneyens Jun 23, 2017

Choose a reason for hiding this comment

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

Here we would profit if had some machinery like mentioned in #1684.

Copy link
Contributor

Choose a reason for hiding this comment

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

@TomasMikula I think we are still tested with Map and Set but I agree tests for overrides would be good.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup. I thought about that too.

@@ -431,6 +431,9 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0
override def fold[A](fa: NonEmptyList[A])(implicit A: Monoid[A]): A =
fa.reduce

override def foldM[G[_], A, B](fa: NonEmptyList[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] =
G.flatMap(f(z, fa.head))(bnext => Foldable[List].foldM(fa.tail, bnext)(f))

Copy link
Collaborator

Choose a reason for hiding this comment

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

NonEmptyList sort of gets this implementation already through NonEmptyReducible, but I guess this saves us the split.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah, forgot about that. A single split call probably doesn't justify the code duplication. I am going to remove this.

override def foldM[G[_], A, B](fa: Stream[A], z: B)(f: (B, A) => G[B])(implicit G: Monad[G]): G[B] = {
def step(in: (Stream[A], B)): G[Either[(Stream[A], B), B]] = {
val (s, b) = in
if(s.isEmpty)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Scalastyle seems to want a space between the if and the (.

@johnynek
Copy link
Contributor

👍

@kailuowang
Copy link
Contributor Author

kailuowang commented Jun 26, 2017

@peterneyens I think addressed your feedback. Kindly take another look?

Copy link
Collaborator

@peterneyens peterneyens left a comment

Choose a reason for hiding this comment

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

Thanks @kailuowang !

@peterneyens peterneyens merged commit 4274102 into typelevel:master Jun 26, 2017
@stew stew removed the in progress label Jun 26, 2017
@kailuowang kailuowang deleted the remove-iteratorFoldM branch June 26, 2017 14:42
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.

6 participants