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

Reply via email to