You could also (dorun (map f coll)) It is actually interesting you brought this up as I was recently contrasting the OO principle "tell don't ask" with the functional way of doing things. In OO a void method taking a visitor is preferred over return values. One could perhaps say that (dorun (map f (generator))) is pretty much the same thing as (each (generator) f) except that generator is now a pure side effect free function where as visitors generally speaking have side effects, although this isn't required for example (each coll identity) is just as easy to use and test.
The more I thought about it the more I began to realize that the OO visitor approach may still have some advantages over the functional approach. In a typed language the generator can overload based on the visitor's type signature, this makes refactoring and evolving code easier while still preserving backward compatibility as different versions of a visitor might have different method parameter types. Of course you could do this in clojure explicitly but is less elegant ie (map f (generator :non-default-type)) and is generally considered confusing to have functions return different return types depending on their parameters. It is probably better to just choose a different function name instead. One other difference that is not as easy to emulate functionally is when the generator spawns multiple threads and delegates the work where each worker may call the visitor back on different threads. In clojure you can easily (pmap f (generator)), however this requires generator to create a sequence of return values from a specific thread regardless of how many threads are used to generate the sequence or process the sequence, ultimately the generator must first bring all these results together on a single thread creating a synchronization bottleneck that does not necessarily need to exist when using side effects instead of return values. For example, the side effect might be to write its results to a database and the generator might partition the work to each visitor never expecting a reply from any of them, the visitor might even forward the request to a different machine on the network since no return value is expected or required. Many will say that side-effecting functions are more difficult to test then pure functions... However after writing about 4000 lines of clojure code, I realized that things in practice are never quite as simple as they seem. As functions are composed the data structures they work with grow larger and more complex and this leads to maps containing maps containing lists containing maps and a minor change downstream can ripple through the program. Tests become significantly more complex and fragile as the input and output structures grow in complexity. This reminded me of another OO code smell.... "Don't talk to strangers" and the Law of Demeter, instead sending and returning maps of lists of maps I started returning maps of functions. This provided additional decoupling that enabled me to refactor a bit more easily additionally maps of maps of lists of maps often need to be fully computed where as a map containing functions allows me to defer computation until it is actually required which may in many cases be never. Although very idiomatic to use keywords to get from maps, I have started to think of this as a code smell and instead prefer to (def value :value) and use this var instead of the keyword because it allows me to later replace the implementation or rename properties if it is necessary to refactor and I want to minimize changes to existing code or make changes to the existing code in small incremental units rather than all at once. It is also very hard to write a useful program that does not contain side-effects or reads data from an impure datasource and I used midje to mock functions that such as (get-current-time), many times my function did not use that function directly but was used by another function that my function called. I began to see this as a code smell in OO I would mock the object that uses get-current-time not get-current-time itself which then gave me the idea to start passing the functions used by the function under test to the function as additional parameters, for testing I could pass in an alternative function such as (constantly true) or (identity). However, some functions required many functions so I began to group these into maps and began to realize that I was reinventing OO programming... perhaps there is a good reason lisp programmers invented CLOS in the first place. I'm starting to go off topic, and don't mean to troll, but your question as innocent as it seems is perhaps one of the most important differences between OO and functional programming ("Tell don't ask"). Perhaps the functional solution is monads... but most find these confusing for some reason and are hardly idiomatic. I digress... perhaps I will write a blog post about the many other patterns and anti-patterns I discovered after working with clojure for a bit. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en