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 Gen.fix to make recursive generators cleaner #616

Merged
merged 1 commit into from
Mar 2, 2020

Conversation

johnynek
Copy link
Contributor

@johnynek johnynek commented Feb 1, 2020

same idea as typelevel/cats#3208 but particular to Gen.

Gen and Parsers are what motivated the 3208 PR.

@ashawley
Copy link
Contributor

ashawley commented Feb 3, 2020

What's the example do? It's building lists that terminate with numbers higher than 5?

val fixGen =
  Gen.fix[List[Int]] { recurse =>
    Gen.choose(0, 10).flatMap { idx =>
      if (idx < 5) recurse.map(idx :: _)
      else Gen.const(idx :: Nil)
    }
  }

@johnynek
Copy link
Contributor Author

johnynek commented Feb 3, 2020

Yes. I didn’t want to make a longer example such as a tree or JSON or something in the comment and I wanted to test the same thing that I put in the comment.

It’s probably not a great example because in this case there are ways to do this without recursion.

@ashawley
Copy link
Contributor

ashawley commented Feb 4, 2020

Yeah, I was also wondering if there was a better example to use. I do like this example for one reason: Writing with existing combinators is clunky, because I tried:

val fixGen = {
  Gen.nonEmptyListOf(Gen.choose(0, 9)).map { ns =>
    val idx = ns.indexWhere(_ >= 5)
    ns.take(if (idx == -1) ns.size - 1 else idx + 1)
  }
}

Maybe there is a built-in generator that could be rewritten using this syntax as an example?

@ashawley
Copy link
Contributor

ashawley commented Feb 4, 2020

This construct seems pretty useful. Should we elevate it by giving it a better name for visibility sake? Could we rename to something like from, of, until or continually?:

val listGen = Gen.of { lists: Gen[List[Int]] =>
  ...
}

The best name I came up with is Gen.fromRecursion but it's disqualified for being too long.

@johnynek
Copy link
Contributor Author

johnynek commented Feb 4, 2020

Fix is the name for this traditionally: fix(f) = f(fix(f)).

I think the name should explain something about recursion. If you aren’t doing recursion you don’t need this.

What about def recursive?

@ashawley
Copy link
Contributor

ashawley commented Feb 4, 2020

I agree. I guess I prefer a verb or an adverb, so even just recur. We can stew on it for a few days. This could be part of 1.15.0 which should be ready soon.

PS. I'm familiar with fixed point since I used to Scheme occasionally. I won't suggest Gen.y, though.

@ashawley ashawley added this to the 1.15.0 milestone Feb 5, 2020
@ashawley
Copy link
Contributor

After some time, I haven't had any strong feelings about a better name, so should we go with Gen.recursive? It works for me.

@larsrh
Copy link
Contributor

larsrh commented Feb 29, 2020

fast-check calls it letrec

@ashawley
Copy link
Contributor

ashawley commented Mar 2, 2020

That would seem to give evidence for naming it recursive then. I'm going to merge and follow-up with a commit that renames it.

@ashawley ashawley merged commit e8212df into typelevel:master Mar 2, 2020
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.

3 participants