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

Implement a ReaderWriterStateT data type #1598

Merged
merged 4 commits into from
May 14, 2017

Conversation

iravid
Copy link
Contributor

@iravid iravid commented Apr 8, 2017

Resolves #1597.

Still missing:

  • Method parity with ReaderT
  • MonadWriter, MonadReader instances
  • tests and laws
  • scaladoc

I'd love to get some feedback on the structure; this is mostly just a re-implementation of methods from StateT, WriterT and ReaderT, which was great for understanding how everything is wired up and is probably more efficient, but I do wonder about the maintainability of this approach.

@codecov-io
Copy link

codecov-io commented Apr 8, 2017

Codecov Report

Merging #1598 into master will increase coverage by 0.05%.
The diff coverage is 94.39%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1598      +/-   ##
==========================================
+ Coverage   93.33%   93.38%   +0.05%     
==========================================
  Files         240      241       +1     
  Lines        3946     4053     +107     
  Branches      142      135       -7     
==========================================
+ Hits         3683     3785     +102     
- Misses        263      268       +5
Impacted Files Coverage Δ
core/src/main/scala/cats/data/package.scala 85.71% <100%> (+2.38%) ⬆️
laws/src/main/scala/cats/laws/discipline/Eq.scala 84.37% <83.33%> (-0.25%) ⬇️
.../src/main/scala/cats/data/ReaderWriterStateT.scala 95% <95%> (ø)
core/src/main/scala/cats/MonadWriter.scala 33.33% <0%> (+33.33%) ⬆️

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 e5ff7d1...28acb4e. Read the comment docs.

@kailuowang
Copy link
Contributor

related #1210

@kailuowang
Copy link
Contributor

@iravid, @edmundnoble is working on a new typeclass encoding for better support MTL, #1210 and some discussion here at gitter provide more context.

@iravid
Copy link
Contributor Author

iravid commented Apr 10, 2017 via email

@edmundnoble
Copy link
Contributor

Just the instances @iravid don't worry, I don't foresee any other changes

@iravid iravid force-pushed the feature/rwst branch 2 times, most recently from 4019c35 to a88ef45 Compare April 12, 2017 06:51
@iravid
Copy link
Contributor Author

iravid commented Apr 12, 2017

@kailuowang can we aim for this to go into 1.0 as well?

@kailuowang
Copy link
Contributor

kailuowang commented Apr 12, 2017

@iravid sure. I only included stuff I liked and/or breaking change. We haven't started the process of surveying what people want for 1.0.0-MF yet (we probably should soon, although I kind of want to figure out the biggest uncertainty - MTL refactor first).

@kailuowang kailuowang added this to the 1.0.0-MF milestone Apr 12, 2017
@iravid
Copy link
Contributor Author

iravid commented Apr 12, 2017 via email

@@ -180,3 +180,20 @@ private[cats] sealed abstract class Unapply3Instances {
def subst: F[AA, BB, C] => M[C] = identity
}
}

private[cats] sealed abstract class Unapply5Instances {
Copy link
Contributor

Choose a reason for hiding this comment

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

Unapply machinery is about to be removed #1583 /cc @kailuowang

@iravid
Copy link
Contributor Author

iravid commented Apr 12, 2017 via email

@iravid
Copy link
Contributor Author

iravid commented Apr 14, 2017

This is now ready for review.

@iravid iravid force-pushed the feature/rwst branch 3 times, most recently from c205e5a to 8601f42 Compare April 14, 2017 14:49
@iravid
Copy link
Contributor Author

iravid commented Apr 20, 2017

Any chance this could get reviewed?

@kailuowang
Copy link
Contributor

kailuowang commented Apr 20, 2017

@iravid I haven't looked at this yet, but I am curious if this better resides in the new cats-mtl/transmogrify. see #1616 cc @edmundnoble

@iravid
Copy link
Contributor Author

iravid commented Apr 21, 2017 via email

@wedens
Copy link
Contributor

wedens commented Apr 21, 2017

I agree that cats-mtl/transmogrifier should be for typeclasses, not for specific data structures.

@iravid iravid force-pushed the feature/rwst branch 2 times, most recently from 035fab0 to 3a27f48 Compare April 21, 2017 09:35
@iravid
Copy link
Contributor Author

iravid commented Apr 21, 2017

Rebased and removed Unapply instance that was added

@kailuowang
Copy link
Contributor

@iravid @wedens my secret agenda is more about keeping cats.core lean. This data type is clearly useful, I am curious is it more useful with MTL typeclasses (I never used it for real)? Obviously if it's commonly used without MTL typeclasses it makes sense for it to stay here.

@iravid
Copy link
Contributor Author

iravid commented Apr 21, 2017 via email

@iravid
Copy link
Contributor Author

iravid commented May 5, 2017

Ping :-) Can we push this forwards?

@kailuowang
Copy link
Contributor

kailuowang commented May 5, 2017

@iravid, not sure if you noticed, build on scala 2.10.6 is broken
UPDATE: it's due to the fact that the build was taking too long. For now I am going to restart it.

@iravid
Copy link
Contributor Author

iravid commented May 5, 2017

Thanks!

@iravid
Copy link
Contributor Author

iravid commented May 5, 2017

Looks green, fortunately :-)

@kailuowang
Copy link
Contributor

@iravid first of all thanks a lot for being patient with us and this big contribution. Right now we have quite a PR backlog. I am not familiar with the subject enough to review this (although if you want, adding some documentation to the datatypes section on the website might help, e.g. use cases, examples, paper reference etc.). If you'd like to accelerate the review process, mentioning it on gitter might help.

@iravid
Copy link
Contributor Author

iravid commented May 6, 2017

Sure, I'll try asking for a review on gitter.

Copy link
Contributor

@edmundnoble edmundnoble left a comment

Choose a reason for hiding this comment

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

After a first pass, this looks good. Since it's so large, I'll do another tomorrow.

implicit def L: Monoid[L]
type TC[F[_]] = Applicative[F]

def liftT[F[_], A](fa: F[A])(implicit F: Applicative[F]): RWST[F, E, S, L, A] =
Copy link
Contributor

Choose a reason for hiding this comment

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

This is just duplicated from RWST.lift.

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! Will delegate.

Copy link
Contributor

@edmundnoble edmundnoble left a comment

Choose a reason for hiding this comment

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

Only possible things I can think of are tests that ask >> ask has the same result as ask, or the reader law, and that local actually modifies the context. Otherwise very well done, and great coverage.

@iravid
Copy link
Contributor Author

iravid commented May 9, 2017

Thanks @edmundnoble. I'll add those additional tests and the Reader law and ping you.

Copy link
Contributor

@edmundnoble edmundnoble left a comment

Choose a reason for hiding this comment

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

@iravid The tests I mentioned are actually MonadReader laws, which you test already. LGTM.

@iravid
Copy link
Contributor Author

iravid commented May 10, 2017

Good point ;) Been a while since I looked at the code. Thanks! @kailuowang I assume we need another 👍 to merge, right?

@kailuowang
Copy link
Contributor

@iravid hmm, for a PR as large as this, we could use more pairs of eyeballs.

We are currently following a practice of requiring at least two sign-offs to merge PRs (and for large or contentious issues we may wait for more).

@iravid
Copy link
Contributor Author

iravid commented May 10, 2017 via email

* In other words, it is a pre-baked stack of `[[ReaderT]][F, E, A]`, `[[WriterT]][F, L, A]`
* and `[[StateT]][F, S, A]`.
*/
final class RWST[F[_], E, S, L, A](val runF: F[(E, S) => F[(L, S, A)]]) extends Serializable {
Copy link
Member

Choose a reason for hiding this comment

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

Would it be better to have ReaderWriterStateT be the canonical name, with RWST as an alias? I look at this as being similar to FunctionK and ~>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed

* Combine this computation with `rwsb` using `fn`. The state will be be threaded
* through the computations and the log values will be combined.
*/
def map2[B, Z](rwsb: RWST[F, E, S, L, B])(fn: (A, B) => Z)(implicit F: FlatMap[F], L: Semigroup[L]): RWST[F, E, S, L, Z] =
Copy link
Member

Choose a reason for hiding this comment

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

Is there an advantage to having this as a separate implementation, rather than just straight delegating to flatMap?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, that's an excellent point.

import cats.syntax.either._

/**
* Represents a stateful computation in a context `F[_]`, over state `S`, with an initial environment `E`, an accumulated log `L` and a result `A`.
Copy link
Member

Choose a reason for hiding this comment

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

Minor thing, but I believe in cats the convention is javadoc-style doc comments, rather than scaladoc-style. Thusly:

/**
 * Docs here
 */

Rather than

/**
  * Boo
  */

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@iravid
Copy link
Contributor Author

iravid commented May 12, 2017

Addressed @djspiewak's comments

@ceedubs
Copy link
Contributor

ceedubs commented May 13, 2017

Wow @iravid this is fantastic. Sorry for letting this hang for quite a while.

The only change that I could think of was potentially changing get to getS, but after writing those comments I remembered that ask is used for Reader, so I think that get is probably fine. 👍 from me thanks a lot!

/**
* Get the input state, without modifying it.
*/
def get(implicit F: Functor[F]): ReaderWriterStateT[F, E, S, L, S] =
Copy link
Contributor

Choose a reason for hiding this comment

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

The corresponding method in State is named get, but for RWST I wonder if getS (or perhaps getState) might be a better name. In particular, this will help distinguish between getting the input E and getting the input S.

/**
* Return the input state without modifying it.
*/
def get[F[_], E, S, L](implicit F: Applicative[F], L: Monoid[L]): ReaderWriterStateT[F, E, S, L, S] =
Copy link
Contributor

Choose a reason for hiding this comment

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

Along the lines of my other comment, I wonder if this should be getS.

/**
* Return the input state without modifying it.
*/
def get[E, S, L: Monoid]: ReaderWriterState[E, S, L, S] =
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as others :)

@ceedubs
Copy link
Contributor

ceedubs commented May 13, 2017

@djspiewak @kailuowang could you please take another look now that I think all review suggestions have been addressed?

@djspiewak
Copy link
Member

@ceedubs In my original review, the only thoughts/reservations I had were expressed, and have now been fixed. So I'm 👍 now.

@iravid
Copy link
Contributor Author

iravid commented May 14, 2017

One thing that should be changed is switching the TransLift instance for MonadTrans if that PR is merged before this one.

@edmundnoble edmundnoble merged commit 586ea08 into typelevel:master May 14, 2017
@edmundnoble
Copy link
Contributor

@iravid No need to worry about that :)

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.

7 participants