-
-
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 traverseWithStateM to Traverse #1767
Conversation
What do you think about adding a version that returns the state as well? like |
Codecov Report
@@ Coverage Diff @@
## master #1767 +/- ##
==========================================
+ Coverage 95.26% 95.26% +<.01%
==========================================
Files 265 265
Lines 4283 4286 +3
Branches 97 94 -3
==========================================
+ Hits 4080 4083 +3
Misses 203 203
Continue to review full report at Codecov.
|
You can mostly achieve that with the current function just by pairing your result with the input state:
The only disadvantage is that if you want to rely on the output of |
@andyscott right. thanks for coming up with an example. |
Sure thing. While the incremental state can be retained with the current function, the final output state is inaccessible. I don't know if it's worth it, but I could add a function that returns In this case, the above |
I am leaning towards adding the |
ca96eed
to
431097d
Compare
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 so much! Let me see if I can get one more maintainer to review it in time for 1.0.0-MF
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'm a bit negative on this method. What does it add vs the user calling this themselves? Can we show important examples where this method can be optimized?
This method isn't really for me! It mainly struck me as a bit cleaner to use and easier to work with for newer functional programmers. In this particular case, it removes a mental dependency on the state monad for the end user. // note: haven't actually compiled this
// ... and it may not be the best example...
case class Record(id: Long, /* ... */)
case class S(index: Int, ids: Set[Long])
def doWork(in: Record): F[Result] = // ...
val ins: List[Record] = // ...
// with helper method
val outs1: F[List[Result]] =
ins.statefulTraverse(S(0, Set.empty))(
(rec, s) =>
if (s.ids.contains(rec.id)) F.pure(DuplicateResult)
else doWork(rec, s.index) )(
(rec, s, _) => s.copy(
index = s.index + 1,
ids = s.ids + rec.id) )
// without helper method
val outs2: F[List[Result]] =
ins.traverse(rec => StateT[F, S, String](s => {
val fb =
if (s.ids.contains(rec.id)) F.pure(DuplicateResult)
else doWork(rec, s.index))
fb.map(_ => s.copy(
index = s.index + 1,
ids = s.ids + rec.id) -> b)
}).runA(S(0, Set.empty)) Some of the recent index related additions to |
Sorry to butt in, but wouldn't this be formulated more clearly with |
E.g. ins.foldLeftM(S(0, Set.empty)) { (s, rec) =>
val fb =
if (s.ids.contains(rec.id)) F.pure(DuplicateResult)
else doWork(rec, s.index))
fb.map(_ => s.copy(
index = s.index + 1,
ids = s.ids + rec.id) -> b)
} (sorry, was on mobile yesterday) |
The posted code example with With |
That's true. Thanks for the correction. |
Sure thing. You prompted me to think about the problem a bit more, which is always a good thing 😄 👍. |
@johnynek is @andyscott's argument above convincing enough for you? If not, I am going to unschedule it from 1.0-RC1, we can add it through extension in a separate module or project. |
I'm not too concerned about this anymore (I'm indifferent if we merge it or
not).
…On Sep 26, 2017 08:19, "Kai(luo) Wang" ***@***.***> wrote:
@johnynek <https://github.com/johnynek> is @andyscott
<https://github.com/andyscott>'s argument above
<#1767 (comment)>
convincing enough for you? If not, I am going to unschedule it from
1.0-RC1, we can add it through extension in a separate module or project.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1767 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAS8WwF0Mk5lnVkhwjODKl__DnccN86_ks5smRX5gaJpZM4Oc8fO>
.
|
Add
traverseWithStateM
andtraverseWithStateMA
to Traverse.This allows you to carry a value through a computation without having to deal with the state monad directly. I have been using this perform multiple stateful computations at once, such as carrying the index value while also marking duplicates. The test I added demonstrates marking for duplicates.
For method naming, I chose
traverseWithStateM
because the "traverse" prefix seems most important. My only minor concern is that the "state" suffix implies the state monad being exposed.