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

Trampolines #30

Closed
edmundnoble opened this issue Jul 31, 2016 · 7 comments
Closed

Trampolines #30

edmundnoble opened this issue Jul 31, 2016 · 7 comments

Comments

@edmundnoble
Copy link
Contributor

All of matryoshka's recursion operators at the moment are not stack-safe. However, always trampolining them can be a performance issue. Therefore, is there any interest in representing recursion schemes in a recursion-less fashion, so that they can be executed in a trampolined or partially-trampolined (a la http://blog.higher-order.com/blog/2015/06/18/easy-performance-wins-with-scalaz/) context?

One way to do this without sacrificing the ability to make plain (not trampolined) recursive calls for data which is known to be small is to write recursion schemes in CPS, such that an ordinary recursive function

def f(a: A): B

is represented as:

def f(a: A, loop: A => Trampoline[B]): Trampoline[B]) 

Then the scheme can make calls to loop instead of explicitly recursing, and it can be executed with each step trampolined, or with none of them trampolined, or with only steps exceeding some maximum stack size trampolined.

@sellout
Copy link
Contributor

sellout commented Aug 2, 2016

Thanks for opening this. It’s certainly something I’ve thought about and others have brought up. It just hasn’t affected us (SlamData) in practice yet, so we haven’t prioritized it.

I think the first step is to come up with some test cases (marked pending initially) that trigger stack overflows that are fixed by trampolining, so we can try a few different approaches.

@edmundnoble
Copy link
Contributor Author

@sellout Makes sense, but it would require coverage of every recursion scheme in the entire library, seeing as they can all stack overflow. Perhaps migrating to a uniform representation of recursion schemes which can be tested on its own will be less prohibitive?

@sellout
Copy link
Contributor

sellout commented Apr 17, 2017

Curious on your feelings on this now. We’ve made some significant strides in terms of the way things are defined (although some of it hasn’t yet been PRed).

Almost all Recursive operations are now implemented in terms of cata (and Corecursive/ana), and while the default implementation of cata/ana isn’t stack-safe, they’re overridden in Mu/Nu. Also, if you are using something with direct & eager recursion (like Fix or Cofree or whatever), you can opt to use cataM[Trampoline] explicitly, no?

@edmundnoble
Copy link
Contributor Author

Agreed. cataM[Trampoline] should provide sufficient stack-safety.

@sellout
Copy link
Contributor

sellout commented Apr 17, 2017

Cool, then I’m closing this and will expect specific issues for cases when this isn’t enough.

@sellout sellout closed this as completed Apr 17, 2017
@ahoy-jon
Copy link

ahoy-jon commented Jan 15, 2018

Hi ! cataM[Trampoline] wasn't enough to provide stack safety.

For reference, I let that here :

def cataTrampoline[E[_],A](t: Fix[E])(f: Algebra[E, A])
                  (implicit BT: Traverse[E]):Trampoline[A] = {
  //with regular cata being :
  //t.hylo[E,A](f, x => x.unFix)
  t.hyloM[Trampoline,E,A](
    x => Trampoline.delay(f(x)),
    x => Trampoline.delay(x.unFix))
}

@edmundnoble
Copy link
Contributor Author

cataM[Trampoline] doesn't provide stack-safety, you're right. It uses pure[Trampoline] which is too strict in this case. That cataTrampoline should be fine.

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

No branches or pull requests

3 participants