This is good stuff. I've certainly felt the same way about FP at some 
points--for me, Clojure really illuminates why people thought OO was such a 
good thing 15 years ago. Let me know if you write that blog post.

On Saturday, June 9, 2012 7:47:47 PM UTC-7, Kurt Harriger wrote:
>
>  
> 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