splitWhen & splitWhenM for Foldable#4855
splitWhen & splitWhenM for Foldable#4855TheBugYouCantFix wants to merge 5 commits intotypelevel:mainfrom
Conversation
898c9a7 to
c50d1d5
Compare
|
I guess the methods could return NonEmptyList[List[A]] for more type safety. I noticed that no method of Foldable returns NonEmptyList so I decided to make it a usual List too but it'd be no problem for me to patch it to NonEmptyList |
36fae12 to
319cd2d
Compare
johnynek
left a comment
There was a problem hiding this comment.
This seems like a useful function but it seems to be it belongs on List not on Foldable.
A major motivation for type classes is that the functions can be optimized for the different instances, but here just call toList and then execute the List implementation.
There is no override for efficiency on any other types.
319cd2d to
0d97322
Compare
0d97322 to
39f9ec6
Compare
Makes sense. I could rewrite it to be in List then |
1dd205f to
5ee3767
Compare
|
@johnynek, I have rewritten it to List, please have another look at the changes |
5ee3767 to
adec14b
Compare
c3f8951 to
4654690
Compare
4654690 to
1012418
Compare
|
Thanks @TheBugYouCantFix , @johnynek ! I have a couple of questions regarding this PR:
|
P.S. I'm not sure if |
2103322 to
16a96d8
Compare
16a96d8 to
665d7e6
Compare
| ): G[NonEmptyList[F[A]]] = { | ||
| foldRight(fa, Eval.now(M.pure(NonEmptyList.one(FA.empty[A])))) { (a, evalGnel) => | ||
| evalGnel.map { gnel => | ||
| M.flatMap(f(a)) { isDelimiter => |
There was a problem hiding this comment.
the loss of tailRecM here means this isn't stack safe and will likely cause pain for users when they try to use it with Reader/Writer/State/Kleisli/etc...
There was a problem hiding this comment.
I'm not sure if a truly stack-safe implementation is possible here inside Foldable yet I may be wrong. But if it turns out to be true then we'd probably have to either remove that method or bring it all back to list...
@satorg what do you think?
There was a problem hiding this comment.
I might be getting it wrong, but I couldn't come up with an example where we'd blow the stack here.
- The method uses
Foldable.foldRightonF[_], which engagesEval, which, in turn, is aStackSafeMonaditself:According to the docs, if it usescats/core/src/main/scala/cats/Eval.scala
Lines 392 to 393 in be4a99d
maporflatMapoverEvalonly, then it's probably safe. - When it comes to type
G[_], then it doesn't have to be aMonadat all –Applicativeshould be enough here:Therefore, the transformation is flat – it should be safe as long as the user-provideddef splitWhenM[G[_], A](fa: F[A])(f: A => G[Boolean])(implicit FA: Alternative[F], G: Applicative[G] ): G[NonEmptyList[F[A]]] = { foldRight(fa, Eval.now(M.pure(NonEmptyList.one(FA.empty[A])))) { (a, evalGnel) => evalGnel.map { gnel => G.map2(f(a), gnel) { case (isDelimiter, nel) => if (isDelimiter) FA.empty[A] :: nel else NonEmptyList(FA.prependK(a, nel.head), nel.tail) } } }.value }
fis safe.
There was a problem hiding this comment.
As @johnynek mentioned, it'd blow the stack if G is Kleisli, Writer or Reader. I tried running it with Kleisli on my machine and just 10K element list was enough to get a StackOverflowError
Apparently, String's On the contrary, That said, I don't have a strict opinion on that – just a feeling that the keep-the-match version would be more generic and re-usable. But it's not a "deal-breaker" for me. |
This PR implements a splitWhen method requested in #4543 for Foldable
Additionally it implements its monadic version splitWhenM
The behaviour is aimed to be identical to that of haskell's splitWhen