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 [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en