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