This week I've begun in earnest to learn some Erlang and start using it in a meaningful way.
At this week's session of the Boston Coding Dojo, we set up and started in on our [top-secret] Erlang project. One of the biggest stumbling blocks we came across was when we wanted to do what was essentially a loop. We resisted the tail call recursion. Instead, we refused to believe we needed recursion. We said to ourselves, "There must be some way to just execute some function X number of times. The standard library must have something for this." We looked all through the lists module with no success. After having read a couple of "getting started" guides on Erlang, I felt that constructing our own recursive method was ultimately the proper solution. Eventually we caved and wrote what I feel is some straightforward and concise code:
1 2 3 4 5 6 7 8 9 10 11 |
%% Generate markers and append them to a URL string. markers_list() -> markers_list(3, "http://maps.google.com/static?zoom=14"). markers_list(0, String) -> String; markers_list(N, String) -> markers_list(N - 1, String ++ "&" ++ marker()). marker() -> "marker=34,56". |
The code is simple and it works, but it left something to be desired. We kept having this feeling that we had missed something obvious in Erlang's stdlib, that it shouldn't be this cumbersome to just concatenate a few random strings. We told ourselves that in a larger project we would certainly genericize the behavior in some sort of utility method. I had my doubts about this, but it made me curious. If this was the kind of thing which really lent itself to a utility method, then why didn't something like it already exist in the stdlib?
When I got home I started trying to think of other ways to do this that would make use of built-in features. I tried to think in terms of the ways other languages would accomplish the same thing, and tried to think of list comprehensions that could build the same sort of URL. That's when I realized that our marker() method was really just a generator, in the Python sense of the term. And what we wanted was to fold the results from this generator into a single string. And earlier we said we wanted to genericize this to the point where we could replace marker() with any old function. We wanted a Python-like generator. So with this in mind, I threw together the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
%% Generate markers and append them to a URL string. markers_list() -> lists:foldl(fun(Marker, URL) -> URL ++ Marker end, "http://maps.google.com/static?zoom=14", gen(3, fun marker/0, [])). %% A Python-like generator. This is a finite generator, however. It generates all %% results at once and inserts them into a list. gen(0, _Function, Result) -> Result; gen(N, Function, Result) -> gen(N - 1, Function, Result ++ [Function()]). marker() -> "&marker=34,56". |
What do you think? Is this more readable, or more modular? Which method is better? Is there yet an easier method that doesn't require me to implement my own generator?