-
-
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 MonadRec for Future and reinstate Future tests for JVM #1261
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,4 +47,5 @@ class FutureTests extends CatsSuite { | |
|
||
checkAll("Future[Int]", MonadErrorTests[Future, Throwable].monadError[Int, Int, Int]) | ||
checkAll("Future[Int]", ComonadTests[Future].comonad[Int, Int, Int]) | ||
checkAll("Future", MonadRecTests[Future].monadRec[Int, Int, Int]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this test a reliable test of stack safety? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @johnynek I don't believe so—it looks like it just checks for consistency with |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package cats | ||
package jvm | ||
package tests | ||
|
||
import cats.data.Xor | ||
import cats.laws.discipline._ | ||
import cats.tests.CatsSuite | ||
|
||
import scala.concurrent.{Await, Future} | ||
import scala.concurrent.duration._ | ||
import scala.concurrent.ExecutionContext.Implicits.global | ||
|
||
import org.scalacheck.Arbitrary | ||
import org.scalacheck.Arbitrary.arbitrary | ||
|
||
class FutureTests extends CatsSuite { | ||
val timeout = 3.seconds | ||
|
||
def futureXor[A](f: Future[A]): Future[Xor[Throwable, A]] = | ||
f.map(Xor.right[Throwable, A]).recover { case t => Xor.left(t) } | ||
|
||
implicit def eqfa[A: Eq]: Eq[Future[A]] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we add this but have it take a timeout to the object where we define the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @johnynek That's what I do for Twitter futures in catbird, but there's no chance for Scala.js there, anyway, and I think Scala.js support is the motivation for keeping all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. I didn't know that restriction. |
||
new Eq[Future[A]] { | ||
def eqv(fx: Future[A], fy: Future[A]): Boolean = { | ||
val fz = futureXor(fx) zip futureXor(fy) | ||
Await.result(fz.map { case (tx, ty) => tx === ty }, timeout) | ||
} | ||
} | ||
|
||
implicit val throwableEq: Eq[Throwable] = | ||
Eq.fromUniversalEquals | ||
|
||
// Need non-fatal Throwables for Future recoverWith/handleError | ||
implicit val nonFatalArbitrary: Arbitrary[Throwable] = | ||
Arbitrary(arbitrary[Exception].map(identity)) | ||
|
||
checkAll("Future with Throwable", MonadErrorTests[Future, Throwable].monadError[Int, Int, Int]) | ||
checkAll("Future", MonadRecTests[Future].monadRec[Int, Int, Int]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't this repeated above? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @johnynek There's a bit of duplication here across |
||
} |
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.
shall we add
@tailrec
?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.
@kailuowang This implementation won't actually compile with
@tailrec
, since the recursive call is inside theflatMap
. It is stack-safe, though, thanks to the wayFuture#flatMap
works.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.
@travisbrown is this stack safe on all versions of scala? I thought scala futures could have issues with this (at least in 2.10 or something. This is just a vague recollection).
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.
@johnynek I'm pretty sure that was only an issue with the original Akka
Future
implementation, and that by the time SIP-14 landedflatMap
was stack-safe (thanks to pressure from Twitter and inspired byc.t.u.Future
). I'll double-check that, though.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.
@johnynek I'm not finding the discussion I was thinking of at the moment, but I did try this on 2.9.3:
Neither blows the stack, and
f1
succeeds as expected.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.
Yeah, looks legit. Thanks (I know Twitter's is stack safe, I didn't know scala was).
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.
Maybe a code comment mentioning that this construct is actually stack-safe would be appropriate 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.
@fthomas Makes sense—done.