-
Notifications
You must be signed in to change notification settings - Fork 529
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 two new combinators: cedeMap
and intercede
#3353
base: series/3.x
Are you sure you want to change the base?
Conversation
/** | ||
* Functor map, but causes a reschedule before and after `f` | ||
*/ | ||
def cedeMap[A, B](fa: IO[A])(f: A => B): IO[B] = | ||
(fa <* cede).map(a => f(a)).guarantee(cede) | ||
|
||
/** | ||
* causes a reschedule before and after `fa` | ||
*/ | ||
def intercede[A](fa: IO[A]): IO[A] = | ||
cede *> fa.guarantee(cede) |
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 for opening the PR! What would you think about moving these methods onto the IO
instance, so that instead of
IO.cedeMap(ioa)(a => foo(a))
IO.intercede(ioa)
users can write
ioa.cedeMap(a => foo(a))
ioa.intercede
However, the methods as written here would be an excellent addition to GenSpawn
in the kernel
module.
Let me know if you have any questions!
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.
True! cedeMap
and intercede
as instance methods would be less boilerplate.
/** | ||
* Functor map, but causes a reschedule before and after `f`. For more information, checkout | ||
* `cede` in the companion object. | ||
*/ | ||
def cedeMap[B](f: A => B): IO[B] = | ||
(this <* IO.cede).map(a => f(a)).guarantee(IO.cede) | ||
|
||
/** | ||
* Causes a reschedule before and after `fa`. For more information, checkout `cede` in the | ||
* companion object. | ||
*/ | ||
def intercede: IO[A] = | ||
IO.cede *> this.guarantee(IO.cede) |
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.
Nice!
Would you mind adding versions of these methods to GenSpawn
in the kernel module? These would be more similar to your original definitions, which are in F[_]
and take the effect as an argument (instead of instance methods).
def cede: F[Unit] |
def cedeMap[A, B](fa: F[A])(f: A => B): F[B] = | ||
for { | ||
a <- fa | ||
_ <- cede | ||
b = f(a) | ||
_ <- cede | ||
} yield b |
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 for adding these! Is there a particular reason you wrote them as for ... yield
? Unfortunately it's not quite correct, because it's missing the guarantee(...)
. Also, in my personal opinion, it's a bit harder to read :)
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.
Is there a particular reason you wrote them as for ... yield?
No, it's I just can't find *> and <* combinators here. 🤦
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.
Huh, were they not compiling? They come from here:
import cats.syntax.all._ |
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.
Oh, I missed the fact that MonadCancel is cats Monad! Will change that in a minute.
/** | ||
* Functor map, but causes a reschedule before and after `f` | ||
*/ | ||
def cedeMap[A, B](fa: F[A])(f: A => B): F[B] = | ||
(fa <* cede).map(a => f(a)).guarantee(cede) | ||
|
||
/** | ||
* Causes a reschedule before and after `fa` | ||
*/ | ||
def intercede[A](fa: F[A]): F[A] = | ||
cede *> fa.guarantee(cede) |
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.
Excellent! :)
If you don't mind, there is one more place we need to add these methods, which is as syntax here:
cats-effect/kernel/shared/src/main/scala/cats/effect/kernel/syntax/GenSpawnSyntax.scala
Line 45 in 130fdcc
final class GenSpawnOps[F[_], A, E] private[syntax] (private val wrapped: F[A]) extends AnyVal { |
These should delegate directly to the typeclass instance, instead of duplicating the implementation.
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.
No problem!
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.
Looking great! Just a couple docs tweaks.
/** | ||
* Functor map, but causes a reschedule before and after `f` | ||
*/ |
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.
/** | |
* Functor map, but causes a reschedule before and after `f` | |
*/ | |
/** | |
* Like functor [[map]], but inserts a [[cede]] before and after `f`. Use this when `f` is a long-running, compute-bound operation. | |
* | |
* @see [[cede]] for more details | |
*/ |
/** | ||
* Causes a reschedule before and after `fa` | ||
*/ |
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.
/** | |
* Causes a reschedule before and after `fa` | |
*/ | |
/** | |
* Insert a [[cede]] before and after `fa`. Use this when `fa` involves a long-running, compute-bound operation outside of the monadic context. | |
* | |
* @see [[cede]] for more details | |
*/ |
*/ | ||
def cede: F[Unit] |
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.
*/ | |
def cede: F[Unit] | |
* | |
* @see [[cedeMap]], [[intercede]] | |
*/ | |
def cede: F[Unit] |
{ | ||
val result = target.intercede | ||
result: F[A] | ||
} |
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.
Excellent, thanks for adding this test! Would you mind adding a test for cedeMap
as well?
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.
Actually the reason that I leave out the test for cedeMap
is I don't know how to obtain f: A => B
in test. I might need some time on getting familiar with how specs2 work. 😂
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.
@biuld just add f: A => B
to the function arguments here:
def genSpawnSyntax[F[_], A, B, E](target: F[A], another: F[B])(implicit F: GenSpawn[F, E]) = { |
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.
Done.
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 for all your good work on this! But someone else should bikeshed the names because I just made them up :)
So I genuinely love the idea of these combinators. I'm not 100% sold on the API, but I'm thinking about it. |
Going to punt these from 3.5 just to give us a bit more time to bikeshed. Like I said, I absolutely love the concept I just want to make sure we get the API right (not just the names, but also the ergonomics). Outstanding work, again! |
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.
Leaving my two cents:
- Personally I would prefer a more intuitive name like
computeMap
overcedeMap
. Of course, the Scaladoc should mention and linkcede
. - In a similar vein, I would like to see two other static combinators like:
object IO {
def compute[A](thunk: => A): IO[A] =
IO.cede >> IO.pure(thunk) >> IO.cede // Not sure if delay + guarantee would be better.
def attemptCompute[A](thunk: => Either[Throwable, A]): IO[A] =
IO.cede >> IO.fromEither(thunk) >> IO.cede // ditto delay + guarantee.
}
These may be in another PR tho.
Especially since those should also be added toAsync
This pull request resolves #3318.