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 contains_, foldSmash and mkString_ to FoldableOps #2123

Merged
merged 9 commits into from
Mar 16, 2018
Merged

Add contains_, foldSmash and mkString_ to FoldableOps #2123

merged 9 commits into from
Mar 16, 2018

Conversation

rsoeldner
Copy link
Contributor

@rsoeldner rsoeldner commented Dec 20, 2017

#2113, Am I right, test should be added to the FoldableSuite ?

@rsoeldner rsoeldner changed the title #2113 Add contains, foldSmash and mkString Add contains, foldSmash and mkString to Foldable Dec 20, 2017
def foldSmash[A](fa: F[A])(prefix: A, delim: A, suffix: A)(implicit M: Monoid[A]): A =
M.combine(prefix, M.combine(intercalate(fa, delim), suffix))

def mkString[A: Monoid](fa: F[A])(prefix: String, delim: String, suffix: String)(implicit F: Functor[F], S: Show[A], ev1: Monoid[String]): String =
Copy link

@chuwy chuwy Dec 20, 2017

Choose a reason for hiding this comment

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

Hey @rsoeldner,

Actually, for mkString I believe we can also generalize over String, so instead foldSmash would work with String as A. Something like:

def mkString[A: Monoid, B: Show](fa: F[A])(prefix: B, delim: B, suffix: B)(implicit F: Functor[F], S: Show[A]): String =
  foldSmash(F.map(fa)(S.show))(prefix.show, delim.show, suffix.show)

Maybe this is bit a bike-shedding, but probably can help if you want to have unusual Show[B] instance in scope - can imagine this helpful with debugging.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, I thought about this, but wasn't able to find a use-case. Do you have an Idea ?

Copy link

Choose a reason for hiding this comment

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

I guess the main question here is "why I might want to pass different Bs into mkString". I think it has use-case for pretty printers to output some structure differently based on environment:

structure.mkString(LeftPad(0), LeftPad(5), LeftPad(0))

Maybe this is a completely made-up example, but why to constrain it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see your point, should we support different types for prefix, suffix and delim ?

Copy link

Choose a reason for hiding this comment

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

Don't know, for me it looks like a skew into "over-generalized" approach, but let's see what community says - maybe even suggestion with Show[B] is unnecessary.

Copy link
Contributor

@johnynek johnynek Jan 10, 2018

Choose a reason for hiding this comment

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

we shouldn't need Functor[F]. You can do this

def mkString[A:Show, B: Show](fa: F[A])(prefix: B, delim: B, suffix: B): String =
  foldLeft(fa, prefix.show) { (left, a) =>
    left + (delim.show) + a.show
  } + (suffix.show)

of course, that is a O(N^2) implementation, but it could be fixed using a StringBuilder:

def mkString[A:Show, B: Show](fa: F[A])(prefix: B, delim: B, suffix: B): String = {
  val bldr = new java.lang.StringBuilder
  bldr.append(prefix.show)
  val b2 = foldLeft(fa, bldr) { (b, a) =>
    b.append((delim.show) + a.show)
  }
  b2.append(suffix.show)
  b2.toString
}

Copy link
Contributor

Choose a reason for hiding this comment

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

that said, I think B is overgeneralized. Users should just do prefix.show etc... and keep it to be String.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After some time, I think you are right. 👍

@kailuowang
Copy link
Contributor

Thanks @rsoeldner will you be interested in taking this approach to avoid breaking binary compatibility?

@rsoeldner
Copy link
Contributor Author

@kailuowang Yes, I'll try come up with a PR tomorrow ;)

@rsoeldner
Copy link
Contributor Author

rsoeldner commented Jan 11, 2018

@kailuowang Is it possible to reformat the entire project ? I used IJ's format functionality and now everything looks different 😭

@kailuowang
Copy link
Contributor

kailuowang commented Jan 25, 2018

@rsoeldner sorry I wasn't sure if that's a question. Certainly we want to avoid any unnecessary reformat. This pr would be a great addition. Fyi now #2148 provides a good example on how to add functions without breaking bin compat.

@kailuowang kailuowang added this to the 1.1 milestone Jan 31, 2018
@kailuowang
Copy link
Contributor

@rsoeldner now I think about it, maybe we should just move the implementations into FoldableOps.
Since we can't let user override them in instances (due to binary compat, we can't add these methods to the trait), we probably shouldn't make them appear to be available from the type class itself anyway.
Let me know if you are still interested/available in continue on this, I'll be more than happy to continue it for you.

@codecov-io
Copy link

codecov-io commented Mar 8, 2018

Codecov Report

Merging #2123 into master will increase coverage by 0.12%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2123      +/-   ##
==========================================
+ Coverage   94.66%   94.79%   +0.12%     
==========================================
  Files         328      332       +4     
  Lines        5533     5702     +169     
  Branches      199      214      +15     
==========================================
+ Hits         5238     5405     +167     
- Misses        295      297       +2
Impacted Files Coverage Δ
core/src/main/scala/cats/syntax/foldable.scala 100% <100%> (ø) ⬆️
core/src/main/scala/cats/data/WriterT.scala 91.37% <0%> (-1.76%) ⬇️
core/src/main/scala/cats/data/Ior.scala 98.48% <0%> (-1.52%) ⬇️
.../src/main/scala/cats/syntax/applicativeError.scala 100% <0%> (ø) ⬆️
core/src/main/scala/cats/syntax/option.scala 100% <0%> (ø) ⬆️
core/src/main/scala/cats/data/package.scala 87.5% <0%> (ø) ⬆️
core/src/main/scala/cats/syntax/TrySyntax.scala 100% <0%> (ø)
...c/main/scala/cats/free/ContravariantCoyoneda.scala 100% <0%> (ø)
core/src/main/scala/cats/data/AndThen.scala 96.96% <0%> (ø)
core/src/main/scala/cats/data/NonEmptySet.scala 97.29% <0%> (ø)
... and 7 more

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 5051b36...3907443. 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.

Elsewhere in cats, namely Traverse, we use the _ to indicate we want the contained type to be Unit. Can we instead name these differently. Maybe mkShowString and containsEq? Just an idea?

* res0: String = List(1,2,3)
* }}}
*/
def mkString_[A: Monoid](prefix: String, delim: String, suffix: String)(implicit F: Functor[F], S: Show[A], ev1: Monoid[String], ev2: Foldable[F]): String =
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we not take Functor on this? If we implemented directly from foldLeft we could. Shame to take an additional constraint just to make implementation easier.

@kailuowang
Copy link
Contributor

Elsewhere in cats, namely Traverse, we use the _ to indicate we want the contained type to be Unit

I am aware of that as well. However I also noticed that there are already filter_, takeWhile_, dropWhile_ in Foldable , so at least in Foldable there is already this mini convention of using _ to avoid conflict. I don't mind renaming them, however mkShowString, containsEq sounds a bit awkward. How about makeString or mkShow and has ? The downside is that they are harder to discover.

@kailuowang kailuowang changed the title Add contains, foldSmash and mkString to Foldable Add contains_, foldSmash and mkString_ to FoldableOps Mar 9, 2018
@kailuowang
Copy link
Contributor

@johnynek WDYT about my comment above?

@rsoeldner
Copy link
Contributor Author

@kailuowang had a small break 👍 thanks for pushing this forward

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.

these changes look good to me with one comment.

builder append A.show(a) append delim
}
if (b.isEmpty)
""
Copy link
Contributor

Choose a reason for hiding this comment

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

don't we need the prefix and suffix here? Can we have a test that exercises this against scala's mkString in the same case?

Copy link
Contributor

Choose a reason for hiding this comment

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

oops. yeah, I meant to change that but slipped. will update now.


test(s"Foldable[$name] mkString_") {
forAll { (fa: F[Int]) =>
fa.mkString_("L[", ";", "]") should === fa.mkString("L[", ";", "]")
Copy link
Contributor

Choose a reason for hiding this comment

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

now is the right hand side working? don't we need a toList or something?

Copy link
Contributor

Choose a reason for hiding this comment

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

good catch. Since this is not my repo I can only edit files through github and my mental compiler is clearly not good enough.

johnynek
johnynek previously approved these changes Mar 14, 2018
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! looks good.

Copy link
Contributor

@ceedubs ceedubs 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 @rsoeldner!

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