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

Cats Effect: STM & TRef #791 #796

Merged
merged 12 commits into from Apr 30, 2019
Merged

Cats Effect: STM & TRef #791 #796

merged 12 commits into from Apr 30, 2019

Conversation

VladKopanev
Copy link
Contributor

Cats Effect: STM & TRef #791

@jdegoes
Copy link
Member

jdegoes commented Apr 25, 2019

Exactly what I was hoping for! I'll comment more when you're ready, but this is looking great.

@VladKopanev
Copy link
Contributor Author

VladKopanev commented Apr 26, 2019

@jdegoes A couple of question has raised in my head while doing this PR:

  1. Is it okey to put the whole thing in interop-cats module? Should it be somewhere else?
  2. What should I do with documentation on STM and Ref? should I reference origin docs or just fix it regarding that we don't use error type and we operate in abstract F[_] context?
  3. I have implemented FunctionK instance for translation from ZIO to cats IO, I guess we need additional instances for types like monix.Task, Future, so users of those don't need to provide instances by themselves. Is this right?
  4. I also have doubts about implementation of specific methods like STM#collectAll I suspect a possibly big amount of boxing/unboxing there, we could implement it like new STM(ZSTM.collectAll(i.map(_.underlying))) this should decrease number of boxings to 1, but adds 1 iteration through iterable, don't know which implementation is better.

I would appreciate if you could elaborate on this questions and of course leave comments in code if you spot other issues.

@jdegoes
Copy link
Member

jdegoes commented Apr 26, 2019

Is it okey to put the whole thing in interop-cats module? Should it be somewhere else?

Yes, it's ok they are put there (in the stm sub package).

What should I do with documentation on STM and Ref? should I reference origin docs or just fix it regarding that we don't use error type and we operate in abstract F[_] context?

I would reference origin docs (definitely not copy them along).

I have implemented FunctionK instance for translation from ZIO to cats IO, I guess we need additional instances for types like monix.Task, Future, so users of those don't need to provide instances by themselves. Is this right?

This makes sense to me. Of course you can probably do that generically for any F[_] that has cats effect instances, since you can go from/to F[_].

I also have doubts about implementation of specific methods like STM#collectAll I suspect a possibly big amount of boxing/unboxing there, we could implement it like new STM(ZSTM.collectAll(i.map(_.underlying))) this should decrease number of boxings to 1, but adds 1 iteration through iterable, don't know which implementation is better.

One iteration through the iterable is not that significant. It will be much faster doing it this way. ^^^

Thanks for your work on this, will do a PR review later today!

@VladKopanev VladKopanev marked this pull request as ready for review April 26, 2019 19:49
@@ -73,8 +74,8 @@ abstract class CatsInstances extends CatsInstances1 {

implicit def taskEffectInstances[R](
implicit runtime: Runtime[R]
): effect.ConcurrentEffect[TaskR[R, ?]] with SemigroupK[TaskR[R, ?]] =
new CatsConcurrentEffect[R](runtime)
): effect.ConcurrentEffect[TaskR[R, ?]] with SemigroupK[TaskR[R, ?]] with FunctionK[TaskR[R, ?], effect.IO] =
Copy link
Member

Choose a reason for hiding this comment

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

Is FunctionK really intended to be used as an implicit? This would not be true for, e.g., Scalaz's NaturalTransformation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

/**
* See [[scalaz.zio.stm.STM]]
*/
final class STM[F[+ _], +A] private[stm] (private[stm] val underlying: ZSTM[Throwable, A])(
Copy link
Member

Choose a reason for hiding this comment

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

I'd have this class extend AnyVal, which you will be able to do when removing the implicit.

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 would like to make it AnyVal too, but scala compiler says value class may not wrap another user-defined value class, because ZSTM is also an AnyVal. I don't see how to overcome this restriction for now.

* See [[scalaz.zio.stm.STM]]
*/
final class STM[F[+ _], +A] private[stm] (private[stm] val underlying: ZSTM[Throwable, A])(
implicit liftIO: ZIO[Any, Throwable, ?] ~> F
Copy link
Member

Choose a reason for hiding this comment

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

I don't think we need this implicit. We should be able to go directly from ZIO to any F[_] that supports Async (or maybe Effect or ConcurrentEffect), given an implicit Runtime. And we can push that down to atomically since that's the only one that needs 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 found a way to implement atomically using Async and Runtime[Any], and removed all useless FunctionK implicits, please check if it's done right.

(self map (Left[A, B](_))) orElse (that map (Right[A, B](_)))

/**
* See scalaz.zio.stm.STM.unit
Copy link
Member

Choose a reason for hiding this comment

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

No Scaladoc link?

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 did this because in this case sbt doc command fails with err message like Could not find any member to link for "scalaz.zio.stm.STM.unit". I suspect this is because it sees unit in the STM class and also in companion object, and fails to resolve which unit to link.

Copy link
Member

Choose a reason for hiding this comment

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

No worries!

final def unit: STM[F, Unit] = const(())

/**
* See scalaz.zio.stm.STM.unit
Copy link
Member

Choose a reason for hiding this comment

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

No Scaladoc link?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

same problem here.


object STM {

final def succeed[F[+ _], A](a: A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] = new STM(ZSTM.succeed(a))
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.


final def succeed[F[+ _], A](a: A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] = new STM(ZSTM.succeed(a))

final def atomically[F[+ _], A](stm: STM[F, A])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): F[A] =
Copy link
Member

Choose a reason for hiding this comment

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

This can make do with an implicit Runtime[Any], then no need for implicit FunctionK.

final def atomically[F[+ _], A](stm: STM[F, A])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): F[A] =
liftIO(ZSTM.atomically(stm.underlying))

final def check[F[+ _]](p: Boolean)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Unit] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.


final def collectAll[F[+ _], A](
i: Iterable[STM[F, A]]
)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, List[A]] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, List[A]] =
new STM(ZSTM.collectAll(i.map(_.underlying)))

final def die[F[+ _]](t: Throwable)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

final def die[F[+ _]](t: Throwable)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] =
succeedLazy(throw t)

final def dieMessage[F[+ _]](m: String)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

final def dieMessage[F[+ _]](m: String)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] =
die(new RuntimeException(m))

final def fail[F[+ _]](e: Throwable)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

final def fail[F[+ _]](e: Throwable)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] =
new STM(ZSTM.fail(e))

final def foreach[F[+ _], A, B](
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

)(f: A => STM[F, B])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, List[B]] =
collectAll(as.map(f))

final def fromEither[F[+ _], A](e: Either[Throwable, A])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

final def fromEither[F[+ _], A](e: Either[Throwable, A])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
new STM(ZSTM.fromEither(e))

final def fromTry[F[+ _], A](a: => Try[A])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

final def fromTry[F[+ _], A](a: => Try[A])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
new STM(ZSTM.fromTry(a))

final def partial[F[+ _], A](a: => A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

final def partial[F[+ _], A](a: => A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
fromTry(Try(a))

final def retry[F[+ _]](implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] = new STM(ZSTM.retry)
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.


final def retry[F[+ _]](implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Nothing] = new STM(ZSTM.retry)

final def succeedLazy[F[+ _], A](a: => A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

final def succeedLazy[F[+ _], A](a: => A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, A] =
new STM(ZSTM.succeedLazy(a))

final def unit[F[+ _]](implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, Unit] = succeed(())
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't need an implicit.

import scalaz.zio.ZIO
import scalaz.zio.stm.{ TRef => ZTRef }

class TRef[F[+ _], A] private (underlying: ZTRef[A])(implicit liftIO: ZIO[Any, Throwable, ?] ~> F) {
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here for the implicit FunctionK. I'd delete that, make TRef extend AnyVal, and push the implicit Runtime[Any] to the commit method. I won't comment individually on these methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed


object TRef {

final def make[F[+ _], A](a: => A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, TRef[F, A]] =
Copy link
Member

Choose a reason for hiding this comment

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

This won't won't need an implicit.

final def make[F[+ _], A](a: => A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): STM[F, TRef[F, A]] =
new STM(ZTRef.make(a).map(new TRef(_)))

final def makeCommit[F[+ _], A](a: => A)(implicit liftIO: ZIO[Any, Throwable, ?] ~> F): F[TRef[F, A]] =
Copy link
Member

Choose a reason for hiding this comment

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

This one will just need the implicit runtime.

Copy link
Member

@jdegoes jdegoes left a comment

Choose a reason for hiding this comment

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

This is looking great! Just a minor comment on how we lift ZIO to some F[_], and once those changes are in, we should be good to merge. 🎉

/**
* See [[scalaz.zio.stm.STM]]
*/
final class STM[F[+ _], +A] private[stm] (private[stm] val underlying: ZSTM[Throwable, A]) {
Copy link
Member

Choose a reason for hiding this comment

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

Looks super clean. I think you can extend AnyVal here, too, now.

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 would like to, but scala compiler has issue with nested AnyVal classes, actually it does not support this feature yet. I didn't find any workarounds at this point. Anyways I think AnyVal has a bunch of restrictions that could make all workarounds meaningless (e.g. passing underlying ZSTM as a trait or other type leads to allocation).


final def succeed[F[+ _], A](a: A): STM[F, A] = new STM(ZSTM.succeed(a))

final def atomically[F[+ _], A](stm: STM[F, A])(implicit R: Runtime[Any], A: Async[F]): F[A] =
Copy link
Member

Choose a reason for hiding this comment

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

Beautiful! Just this single method needs the implicit.


object TRef {

final def make[F[+ _], A](a: => A): STM[F, TRef[F, A]] =
Copy link
Member

Choose a reason for hiding this comment

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

This all looks good to me! I'd just sort the methods alphabetically, then it looks good to merge! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@jdegoes jdegoes merged commit fb7b0f1 into zio:master Apr 30, 2019
@jdegoes
Copy link
Member

jdegoes commented Apr 30, 2019

giphy

Awesome work! Clean code and super fast revisions. Thank you for your contribution!

@VladKopanev VladKopanev deleted the feature/791-stm-interop branch May 1, 2019 00:11
@VladKopanev
Copy link
Contributor Author

Thanks @jdegoes! Please let me know if we need to add TPromise, TQueue and TSemaphore in the same fashion into cats interop module, I can help help with that.

@jdegoes
Copy link
Member

jdegoes commented May 1, 2019

@VladKopanev That would be phenomenal!

Dan-M added a commit to Dan-M/scalaz-zio that referenced this pull request May 2, 2019
* upstream/master:
  Cats Effect: STM & TRef zio#791 (zio#796)
  increase RS TCK timeouts in interopRS tests (zio#806)
  zio#804 Move ArbitraryCause.scala and GenIO.scala to shared (zio#809)
  docfix: Minor scaladoc fix
ghostdogpr pushed a commit to ghostdogpr/scalaz-zio that referenced this pull request May 5, 2019
* Implement effect-polymorphic class atop STM zio#791

* Implement effect-polymorphic TRef zio#791

* remove unnecessary type parameters zio#791

* Add 'FunctionK' instance for converting TaskR to cats.IO zio#791

* Link to original documentation zio#791

* More efficient `collectAll` zio#791

* Make every method final zio#791

* Revert: Add 'FunctionK' instance for converting TaskR to cats.IO zio#791 (37d17db)

* Push implicits down to atomically zio#791

* Reorder methods alphabetically zio#791

* Fix formatting zio#791
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.

None yet

2 participants