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

Some clean up for Free (and friends). #1085

Merged
merged 7 commits into from
Jun 11, 2016

Conversation

non
Copy link
Contributor

@non non commented Jun 2, 2016

This commit does a bunch of small things:

  • Introduces foldLeftM and runTailRec
  • Renames mapSuspension to compile
  • Removes Applicative[F] requirement from Free.suspend
  • Adds some new comments
  • Moves the Free companion to the end of the file
  • Minor formatting changes

I think all of these are good, but I'm happy to revert
any of them that seem like they are bad news. The main
reason I removed mapSuspension is that its description
was written using "compile" which seems like a more
evocative name (there are already several "map" variants).

This commit does a bunch of small things:

 * Introduces `foldLeftM` and `runTailRec`
 * Renames `mapSuspension` to `compile`
 * Removes `Applicative[F]` requirement from `Free.suspend`
 * Adds some new comments
 * Moves the Free companion to the end of the file
 * Minor formatting changes

I think all of these are good, but I'm happy to revert
any of them that seem like they are bad news. The main
reason I removed `mapSuspension` is that its description
was written using "compile" which seems like a more
evocative name (there are already several "map" variants).
@non non added the in progress label Jun 2, 2016
@codecov-io
Copy link

codecov-io commented Jun 2, 2016

Current coverage is 87.96%

Merging #1085 into master will decrease coverage by 0.15%

  1. File ...reeApplicative.scala (not in diff) was modified. more
    • Misses 0
    • Partials 0
    • Hits -1
@@             master      #1085   diff @@
==========================================
  Files           224        224          
  Lines          2844       2848     +4   
  Methods        2787       2791     +4   
  Messages          0          0          
  Branches         52         52          
==========================================
- Hits           2507       2505     -2   
- Misses          337        343     +6   
  Partials          0          0          

Sunburst

Powered by Codecov. Last updated by 3608287...4d548a9

@mandubian
Copy link
Contributor

👍 really good move!

I don't like the Eval.value calls, but I don't know of a way around
them. The unit tests suggest that they aren't a problem due to the
nature of `Free.runTailRec` and `FlatMapRec.tailRecM`.
@ceedubs
Copy link
Contributor

ceedubs commented Jun 3, 2016

Thanks @non! I've submitted non#1 because I think that we can get short-circuiting semantics out of foldLeftM.

Add a short-circuiting implementation of foldLeftM
@non
Copy link
Contributor Author

non commented Jun 3, 2016

@ceedubs Thanks! Great job.

@ceedubs
Copy link
Contributor

ceedubs commented Jun 3, 2016

I'm thinking that we'll probably want to move Free (the monad - not all of the free module) into core before 1.0 so that we can have foldLeftM on Foldable and take similar approaches for some other types. But I think that we should move forward with this in the mean time 👍.

@ceedubs
Copy link
Contributor

ceedubs commented Jun 3, 2016

@mandubian would you mind taking another look now that my change has been included in this PR?

@non
Copy link
Contributor Author

non commented Jun 5, 2016

OK, this should be good to merge once it gets sign-offs.

@mandubian
Copy link
Contributor

@ceedubs code is good to me... Just need to think a bit more about this foldLeftM that is based on a foldRight and that becomes safe due to Free + MonadRec...

@adelbertc
Copy link
Contributor

👍 from me

def foldLeftM[F[_]: Foldable, G[_]: MonadRec, A, B](fa: F[A], z: B)(f: (B, A) => G[B]): G[B] =
unsafeFoldLeftM[F, Free[G, ?], A, B](fa, z) { (b, a) =>
Free.liftF(f(b, a))
}.runTailRec
Copy link
Contributor

Choose a reason for hiding this comment

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

Did I miss it, or does this method not use the G.tailRecM method at all?

Also, it seems like there is double trampolining (once via Free and once via Eval in F.foldRight)? I wonder if a more direct implementation (using either Free or Eval, but not both) is possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

@TomasMikula as the author of this code, I'll be the first to say that it is a bit bizarre. G.tailRecM should be being called by the runTailRec call on the Free structure. Eval makes an appearance due to the foldRight type signature, but really we are just using foldRight because of its laziness, and unsafeFoldLeftM is calling .value on the Eval, so we don't actually get any Eval trampolining here. It's effectively like we are using a scalaz-style foldRight that takes a byname parameter, since we aren't taking advantage Eval's trampolining.

If you can figure out a way to provide a more direct implementation, I'd definitely be interested. I think that this has to be done in terms of foldRight if we want laziness, though, which means Eval will definitely be in play. You may be able to use MonadRec and foldRight without Free, and if so that'd be great - as it stands I'm kind of convinced that we should move Free (the monad but not the other free structures) back into core for this sort of thing.

Copy link
Contributor

@ceedubs ceedubs Jun 11, 2016

Choose a reason for hiding this comment

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

One other note: it really isn't obvious to me that this implementation should be stack safe, but the tests kind of prove that it is ¯\_(ツ)_/¯

Copy link
Contributor

Choose a reason for hiding this comment

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

somehow I completely missed that .runTailRec at the end 😊

@ceedubs
Copy link
Contributor

ceedubs commented Jun 11, 2016

I'm going to go ahead and merge, because this has some really good changes in it. @TomasMikula please feel free to experiment with foldLeftM - it is similar to a few other methods that I think we will want to support under a common approach to stack-safety.

@ceedubs ceedubs merged commit 068bacb into typelevel:master Jun 11, 2016
@TomasMikula
Copy link
Contributor

I might try to experiment to avoid the use of Free in foldLeftM, but I'm afraid that if I succeed I will just reinvent half of it in the process.

@TomasMikula
Copy link
Contributor

@ceedubs so, I was not even trying to move the computation to the type level, but I just managed to cause a stack overflow in the compiler 😄

@TomasMikula
Copy link
Contributor

See #1117.

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