Skip to content

Add guidelines on judicious use of recursion #6

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

nickelization
Copy link

No description provided.

Copy link

@paulhenrich paulhenrich left a comment

Choose a reason for hiding this comment

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

README looks good aside from minor formatting issue. See my notes re. examples.

@@ -0,0 +1,31 @@
-module(recursion).

Choose a reason for hiding this comment

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

Went to go play with this and noticed that the functions aren't exported.

Copy link
Author

Choose a reason for hiding this comment

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

Yeah, good call. I semi-deliberately left out the exports since I was mainly intending for this code to be a concise, illustrative example, rather than something anyone might ever actually want to run. I suppose it'd be easy enough to just add the one export line though and have a working module, so I'll go ahead and do that.

recurse(S) ->
lists:reverse(recurse(S, [])).

recurse([], Acc) ->

Choose a reason for hiding this comment

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

Not sure if this is meant as an illustration of your point (if so 👍 😄 ), but this doesn't work.

Copy link
Author

Choose a reason for hiding this comment

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

Hahaha I wish I could claim credit for intentionally writing a bug in this function, but admittedly I did not actually test the code since I was just trying to convey the gist of the idea, and didn't expect anyone to actually run it 😆 . I will fix it along with addressing your other comment above, and then do a rebase and push.

Additionally, to an experienced Erlang developer, folds and list comprehensions are much easier to understand than complex recursive functions. Such contstructs behave predictably: they always perform an action for each element in a list. A recursive function may work similarly, but it often requires careful scrutiny to verify what path the control flow will actually take through the code in practice.

***
***
Copy link

@paulhenrich paulhenrich Jan 13, 2017

Choose a reason for hiding this comment

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

minor formatting (one too many breaks)

Copy link
Author

Choose a reason for hiding this comment

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

Whoops, nice catch. Thanks! Will also fix this in a minute.

@paulhenrich
Copy link

👍

@tburghart
Copy link

tburghart commented Jan 15, 2017

Isn't this subject already covered somewhere?
I recall something like "don't write your own recursion" in either the inaka version or something from @lehoff

The title's also feels inaccurate - you're not suggesting avoiding recursion, necessarily, just advising against rolling your own.

@nickelization
Copy link
Author

@tburghart I also thought we had something about this already in the guidelines, but it appears we don't. I also don't see anything in the original Inaka version. I know this has come up at meetups before, so maybe we're both just recalling having discussed it.

I'm not sure if I understand the difference between you're trying to point out between avoiding recursion vs. advising against rolling your own. By titling the section "avoid recursion when possible", I meant to imply "avoid writing code using recursion when possible"; I wasn't that explicit, but I'm not sure how else someone would interpret it, or whether adding such verbosity would address your concerns. Could you explain a little more about what you found inaccurate there, and perhaps suggest a better title?

@tburghart
Copy link

tburghart commented Jan 18, 2017

Yeah, there was a separate "architecture guidelines" doc on the wiki somewhere ... maybe I'll see if I can find it.

As for the title, I might be being too pedantic, but Erlang is all about recursion - many of the lists functions are, after all, explicitly recursive. My point was only that we have no desire to avoid recursion, what we want to discourage is people writing their own when stdlib contains a function that will do it for them.

This also conflicts to an extent with our recommendation to use named functions instead of closures when possible, as it's pretty common to have to wrap a function in a fun to fit it into the stdlib recursive functions, and given the choice I'd opt to write my own over using a closure when I don't need the encompassing state.

@nickelization
Copy link
Author

Ahh got it, that makes sense - I hadn't considered the angle of "lists:foldl is recursive" and I certainly wouldn't want to discourage anyone from using that ;). I'll try to think of a way to rewrite the title for extra specificity.

That's an interesting point about named functions vs. closures, I hadn't thought about that particular tradeoff. I mean the ideal scenario is you can pass a named function directly into a library function, e.g. lists:map(fun my_map_fun/1, List), but of course that's not always going to suffice....

My personal feeling is that most of the time I'd still rather we pass a closure into a higher-order function than roll our own recursion, but I can see the argument either way. Perhaps we should defer to the architecture group and see if they can offer any additional thoughts on this.

This should improve clarity, since technically functions like foldl and
map use recursion; we want people to avoid writing their own recursive
functions, not avoid using any function that does recursion under the
hood.

%% GOOD: uses a fold instead to achieve the same result,
%% but this time more safely, and with fewer lines of code
fold(S) ->

Choose a reason for hiding this comment

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

Lists map lists:map(fun string:to_upper/1, S) would make the good example better (and almost the BEST ;-)

Copy link
Author

Choose a reason for hiding this comment

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

Valid point! I had really wanted to demonstrate using a fold in my example, since almost any recursive list iteration function can be converted to a fold, so perhaps I will add a map example alongside fold.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

@JeetKunDoug
Copy link

I think what @tburghart was talking about is mentioned in https://github.com/basho/internal_wiki/wiki/Code-Review-Guidelines#code-clarityquality - we had a long conversation about the wording of it over email, so may be worth taking the wording from there?

@nickelization
Copy link
Author

@JeetKunDoug 👍 I will update the wording to match.

This better conveys when and why you should avoid manual recursion.
@paulhenrich
Copy link

👍

@bashopatricia
Copy link

create jira issue

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