-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Conversation
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 = |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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 B
s 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?
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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. 👍
Thanks @rsoeldner will you be interested in taking this approach to avoid breaking binary compatibility? |
@kailuowang Yes, I'll try come up with a PR tomorrow ;) |
@kailuowang Is it possible to reformat the entire project ? I used IJ's format functionality and now everything looks different 😭 |
@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. |
@rsoeldner now I think about it, maybe we should just move the implementations into |
Codecov Report
@@ 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
Continue to review full report at Codecov.
|
There was a problem hiding this 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 = |
There was a problem hiding this comment.
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.
I am aware of that as well. However I also noticed that there are already |
contains_
, foldSmash
and mkString_
to FoldableOps
@johnynek WDYT about my comment above? |
@kailuowang had a small break 👍 thanks for pushing this forward |
There was a problem hiding this 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) | ||
"" |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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[", ";", "]") |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 LGTM thanks @rsoeldner!
#2113, Am I right, test should be added to the
FoldableSuite
?