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

Add MonadRec for Future and reinstate Future tests for JVM #1261

Merged
merged 2 commits into from
Aug 3, 2016

Conversation

travisbrown
Copy link
Contributor

This PR adds a new MonadRec instance for Future (as discussed on Gitter). The implementation of tailRecM is not literally tail recursive, but it is stack-safe.

I've also reinstated the jvm module that was removed in #1018, since it looks like the removal of the MonadError laws checking there wasn't intentional, and since I need a place to put the MonadRec laws checking for Future.

def pure[A](x: A): Future[A] = Future.successful(x)

def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = fa.flatMap(f)

final def tailRecM[B, C](b: B)(f: B => Future[(B Xor C)]): Future[C] =
Copy link
Contributor

Choose a reason for hiding this comment

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

shall we add @tailrec ?

Copy link
Contributor Author

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 the flatMap. It is stack-safe, though, thanks to the way Future#flatMap works.

Copy link
Contributor

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).

Copy link
Contributor Author

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 landed flatMap was stack-safe (thanks to pressure from Twitter and inspired by c.t.u.Future). I'll double-check that, though.

Copy link
Contributor Author

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:

import scala.concurrent.{ Await, Future }, scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global

def test(i: Int)(f: Int => Future[Int]): Future[Int] = f(i).flatMap {
  case x if x > 100000 => Future.successful(x)
  case x => test(x)(f)
}

val f1 = test(0)(x => Future(x + 1))
val f2 = test(0)(x => Future(x))

Neither blows the stack, and f1 succeeds as expected.

Copy link
Contributor

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).

Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@fthomas Makes sense—done.

@ceedubs
Copy link
Contributor

ceedubs commented Aug 3, 2016

Oops sorry about that, it looks like I did remove some tests that I didn't mean to remove. Thanks for catching this @travisbrown.

I'm a bit confused though. Why is it that these tests need to be jvm-only? One explanation that I could imagine is that the Eq[Future[A]] instance uses Await.result. However, it looks like we are calling Await.result in the JS Future tests, so is that not an issue?

@codecov-io
Copy link

codecov-io commented Aug 3, 2016

Current coverage is 90.32% (diff: 100%)

Merging #1261 into master will increase coverage by 0.18%

@@             master      #1261   diff @@
==========================================
  Files           243        243          
  Lines          3285       3286     +1   
  Methods        3231       3231          
  Messages          0          0          
  Branches         51         52     +1   
==========================================
+ Hits           2961       2968     +7   
+ Misses          324        318     -6   
  Partials          0          0          

Sunburst

Powered by Codecov. Last update d8e2671...cad6429

@travisbrown
Copy link
Contributor Author

@ceedubs The js version of the tests uses a different Await, so having the tests be shared would be somewhat inconvenient (you'd still need some platform-specific code for at least the Eq instance).

@@ -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])
Copy link
Contributor

Choose a reason for hiding this comment

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

is this test a reliable test of stack safety?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 flatMap.

@johnynek
Copy link
Contributor

johnynek commented Aug 3, 2016

👍

Love some MonadRec!

@kailuowang
Copy link
Contributor

👍 thanks a lot !

@ceedubs
Copy link
Contributor

ceedubs commented Aug 3, 2016

@travisbrown thanks for the explanation regarding Await. That makes sense now.

👍 once the build passes. Thanks!

@kailuowang kailuowang merged commit 0791198 into typelevel:master Aug 3, 2016
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.

6 participants