On Fri, 18 Dec 2009 08:55:13 -0800 (PST)
IslandRick <rick.braumoel...@gmail.com> wrote:
> Can anyone here offer some advice to those who are too ingrained in
> using an object-oriented hammer on every nail they see?  I know Rich
> and Stuart have some good design examples around (I've read many), but
> if there are any tutorials that show how to re-envision OO problems in
> an FP world, I'd love to see them.

I haven't seen an answer to this, so here's my one-page guide. This is
meant more for LISP in general than Clojure specific, but should be
better than nothing.


I. Functions

There have been a number of references to the fact that writing
functional programming seems upside-down and/or backwards. That's what
you've got here. Clojure's -> macro may help with that, but I'm going
to avoid it here.

When writing a function in an imperative language, you usually start
with arguments, and then pass them through a series of transforms to
get the value you want to return, like so:

    results1 = step1(arguments)
    results2 = step2(results1)
    return third_step(results2)

To translate that into a function form, either go up from the bottom
of the imperative function as you go inwards in the functional
version, or start at the inside of the functional version as you go
down the imperative one:

   (third_step (step2 (step1 arguments)))

Just like learning a foreign language, if you keep it up you'll
eventually stop thinking in imperative terms and translating to
functional, and start thinking in functional terms. Actually, that's
easier than getting to the point of thinking in a foreign language -
or at least it was for me.


II: Structure

OO programs tend to have objects that invoke each others methods in
order to manipulate state and extract information. The typical syntax
for a method invocation is:

    object.method(args)

Lisp systems have variables - some of which are functions -
instead. So instead of an object that has both it's instance data and
the class methods, you have a LISP data structure that holds the
"instance data" and a set of functions that manipulate it. A typical
invocation (using the names above) looks like:

      (method object args)

As an aside, "class data" winds up in the global variable space.

So to write a "class" in LISP, you'd start with the native data
structure appropriate for the instance variables. Then you'd write
functions that accept one of those as the first argument, and take the
appropriate action.

I.e. - if you stored a bank account in a hashmap with the balance and
id #, you'd do something like (untested code):

(defn make-account [initial-balance id] {:balance initial-balance :id id})
(defn deposit [account amount] (make-account (+ (:balance account) amount) id))
(defn withdraw [account amount] (make-account (- (:balance account) amount) id))
(defn balance [account] (:balance account))

and then do (deposit my-account 20) to invoke the deposit "method" on
my-account and get an updated account, and so on. It's then up to the
caller to do whatever is required with the account to make the state
persistent (update a ref, write it to a database, whatever).

If you really need to have the methods and data in one object that
appears first in the invocation, you can capture them in a
closure. Here's a version of the bank account object with an (object
:method args) version of the API:

(defn make-account [initial-balance id]
  (let [methods {:balance (fn []
                            initial-balance)
                 :deposit (fn [amount]
                            (make-account (+ initial-balance amount) id))
                 :withdraw (fn [amount]
                             (make-account (- initial-balance amount) id))}]
    (fn [method & args]
      (apply (methods method) args))))

which would then be used as (my-account :deposit 20), etc.

Note that LISP systems with OO subsystems tend to keep the
method-first invocation. But they also only seem to be used by a
fringe of the LISP community.

I'm not going to talk about inheritance - it's sensitive to the
underlying LISP, and I don't really know enough about Clojure to do so
intelligently.



   HTH,
   <mike
-- 
Mike Meyer <m...@mired.org>             http://www.mired.org/consulting.html
Independent Network/Unix/Perforce consultant, email for more information.

O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

-- 
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