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 Foldable.intercalate #1520

Merged
merged 3 commits into from
Jan 7, 2017

Conversation

peterneyens
Copy link
Collaborator

Add an element between all the existing elements while folding.

@codecov-io
Copy link

codecov-io commented Jan 6, 2017

Current coverage is 92.41% (diff: 100%)

Merging #1520 into master will increase coverage by <.01%

@@             master      #1520   diff @@
==========================================
  Files           246        246          
  Lines          3739       3743     +4   
  Methods        3618       3622     +4   
  Messages          0          0          
  Branches        121        121          
==========================================
+ Hits           3455       3459     +4   
  Misses          284        284          
  Partials          0          0          

Powered by Codecov. Last update 468e753...d02699e

@ceedubs
Copy link
Contributor

ceedubs commented Jan 6, 2017

Thanks @peterneyens. This looks good to me. The string example is the main way I could see myself using this in the real world. Out of curiosity, did you have any other use-cases in mind?

If we add this, should we add something like intercalate1 (to follow the naming convention of traverse1_ and sequence1_) to Reducible that requires only a Semigroup as opposed to a Monoid?

@peterneyens
Copy link
Collaborator Author

I added Reducible.intercalate1, forgot last night that we could lower the requirement to Semigroup for Reducible.

@ceedubs
Copy link
Contributor

ceedubs commented Jan 6, 2017

@peterneyens would you be willing to add a test for intercalate1? It'd probably be nice to hit the overridden intercalate in Reducible too.

@ceedubs
Copy link
Contributor

ceedubs commented Jan 6, 2017

Actually I guess the sbt-doctest examples are pretty legitimate tests. 👍 from me.

* res0: String = a-b-c
* scala> Foldable[Vector].intercalate(Vector(1,2,3), 1)
* res1: Int = 8
* }}}
Copy link
Contributor

Choose a reason for hiding this comment

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

It is not clear what we should expect if there is only 1 item in the foldable (nor is it tested). I would expect no additional item is added. I think that is what the code does but can you add a doc test example as well as a test?

@@ -67,6 +67,14 @@ abstract class FoldableCheck[F[_]: Foldable](name: String)(implicit ArbFInt: Arb
fa.reduceRightOption((x, ly) => ly.map(x - _)).value should === (list.reduceRightOption(_ - _))
}
}

test("intercalate") {
forAll { (fa: F[Int], a: Int) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

This test might be clearer if you tested against mkString. Basically implement mkString with intercalate.

Also, using a commutative monoid here (on Int) weakens the test. Using String, which is not commutative would make it stronger.

@ceedubs
Copy link
Contributor

ceedubs commented Jan 6, 2017

Good points @johnynek.

@tpolecat
Copy link
Member

tpolecat commented Jan 6, 2017

@peterneyens yeah I need it for generating SQL … I also did a more general thing that takes start/end brackets as well as the delimiter, which generalizes mkString and is useful but it's hard to think of a good name.

In any case 👍 for this, with test improvements as suggested above.

@adelbertc
Copy link
Contributor

👍 after test improvements.

@peterneyens
Copy link
Collaborator Author

Thanks for the pointers @johnynek

  • I used mkString and F[String] in the tests instead.
  • I added a test for intercalate1 (it was already tested before through the overridden intercalate which is tested because ReducibleCheck extends FoldableCheck, but is probably better to be explicit).
  • I added a doctest for intercalate and intercalate1 with a one item foldable. The one element foldable case is already covered by the property tests, right ?

@johnynek
Copy link
Contributor

johnynek commented Jan 7, 2017

+1 on this.

@adelbertc
Copy link
Contributor

Let's do this

@adelbertc adelbertc merged commit fd15e07 into typelevel:master Jan 7, 2017
@stew stew removed the in progress label Jan 7, 2017
@johnynek
Copy link
Contributor

johnynek commented Jan 9, 2017

one bummer about this: using it to write mkString will be O(N^2) in cost. This is one of the problems that combineAll was designed to deal with. Not sure the best way to go here. For integers it is a waste, but for a lot of strings a huge win.

The simplest thing would be to use Foldable.toList to build a list, then intercalate only on the list, then call combineAll on the list. I guess that might be the right way to go actually.

@peterneyens
Copy link
Collaborator Author

@johnynek, so something like the following ?

def intercalate[A](fa: F[A], a: A)(implicit A: Monoid[A]): A =
  combineAll(intersperseList(toList(fa), a))

private def intersperseList[A](xs: List[A], x: A): List[A] = {
  val bld = List.newBuilder[A]
  val it = xs.iterator
  if (it.hasNext) {
    bld += it.next
    while(it.hasNext) {
      bld += x
      bld += it.next
    }
  }
  bld.result
}

// Reducible 
def intercalate1[A](fa: F[A], a: A)(implicit A: Semigroup[A]): A =
  reduce(intersperseList(toList(fa), a))

@johnynek
Copy link
Contributor

johnynek commented Jan 9, 2017 via email

@peterneyens peterneyens deleted the foldable-intercalate branch January 16, 2017 15:53
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.

8 participants