I don't know if anybody mentioned this before, (it was not suggested to me
when I asked a similar question earlier),
but Stuart's explanation on OOP in Clojure is the best I've ever found.
It's simple, clear and takes on each of the 4 pieces nicely.

http://blog.thinkrelevance.com/2009/8/12/rifle-oriented-programming-with-clojure-2

On Sat, Dec 19, 2009 at 5:45 PM, Mike Meyer <
mwm-keyword-googlegroups.620...@mired.org> wrote:

> 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<clojure%2bunsubscr...@googlegroups.com>
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
>

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