OO Programmer trying to move to Clojure: Encapsulation

640 views
Skip to first unread message

Dru Sellers

unread,
Feb 7, 2015, 11:07:45 AM2/7/15
to clo...@googlegroups.com
Greetings,

I am trying to convert my mind from OO (C#) to one more functionally friendly. I am increasingly comfortable with simple applications in clojure, but as I start to build more complex applications, I start to fall down about how to structure my application. I don't want to just shove OO ideas into the functional nature of Clojure. I'm looking for any help someone can provide to help shimmy my brain into the right mental patterns.

Background: Long time C# developer who leans heavily on IoC / DI / Interfaces / Testing / Object Encapsulation.


Specific Question: Object Encapsulation
I feel like my function parameter lists are much "wider" than they would be in C#. This is due to the "lack" of a constructor. So if I previously had a C# class that looked like:


public class AccountRepository : IAccountRepository
{
  IDatabaseConnection _connection;

  public AccountRepository(IDatabaseConnection connection)
  {
    _connection = connection;
  }

  public void Save(Account account)
  {
    _connection.Execute("UPDATE accounts SET coupon=@coupon WHERE id=@id", { coupon = account.Coupon, id=account.Id});
  }
}

In the above I have encapsulated the "_connection", the other methods don't have to deal with it. In Clojure (especially if you are following Sierra's Component pattern) you end up with 

(defn save [db account]
  (update db (:coupon account) (:id account))
)

I end up having to pass this 'db' around all over the place. I think that this is just something I'll have to get used to more than anything. Am I missing anything?

Thank you for your time.

-d

James Reeves

unread,
Feb 7, 2015, 9:15:35 PM2/7/15
to clo...@googlegroups.com
I think there's less difference than you imagine. In C# you might write:

    repository.Save(account)

Whereas in Clojure:

    (save repository account)

The parameter lists are wider in Clojure only because Clojure doesn't have an explicit method call syntax.

- James

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@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+u...@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 unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jony Hudson

unread,
Feb 8, 2015, 9:21:09 AM2/8/15
to clo...@googlegroups.com
To put take a different angle on it: you might soon get to *like* that it's usual to pass in everything as a parameter explicitly. It has the advantage that it's easy to reason about what a function does just by looking at the function. In the example above, when reading AccountRepository.Save you need to check elsewhere to see how _connection is defined. Whereas with the function save, so long as the parameters you feed in are sensible, then you can be pretty sure what it will do.

Making the functions self-contained like this is also a boon when it comes to working at the REPL. The OO approach you have often makes it difficult (for less trivial examples) to run a particular function in the right context. But with the self-contained function it's just a case of getting the parameters right, which is often easier to think about.


Jony

Colin Yates

unread,
Feb 8, 2015, 12:16:40 PM2/8/15
to clo...@googlegroups.com
+1 This separation of behaviour and state is a key part of Clojure's
philosophy. That isn't to say that stateful components are bad as such
(Stuart Sierra's https://github.com/stuartsierra/component is an
obvious analog here) only that they aren't a given as they are in OO
languages.

As Jony says, when functions only depend on their parameters it makes
many things simpler and more flexible, but can appear 'noisy'.

http://thinkrelevance.com/blog/2009/08/12/rifle-oriented-programming-with-clojure-2
is another good resource here, although I must say I haven't seen some
of those examples in the wild :).

From a similar background (Java) and having learnt some lessons, I
wish somebody hammered me on the head with the following:
- get real familiar with the notion of data transformations, particularly maps
- internalise map, reduce, filter, apply, loop/recur and into
- before writing any code check the API doesn't already provide it
- repeat the above three points
- embrace vanilla maps, they really are all you need (and also watch
https://www.youtube.com/watch?v=ZQkIWWTygio)
- small functions (10 lines is uncomfortable) always
- embrace pre/post conditions, documentation (defn doc and
marginalia) and prismatic schema
- read as much Clojure code as you can get your hands on
- TDD is still a helpful process/technique in Clojure ;)

Longer term, watch anything from Rich Hickey.

By far the biggest conceptual shift was thinking in terms of data
being the public API and data transformation over blobs of state and
data.

Hope this helps.

Colin Yates

unread,
Feb 8, 2015, 12:47:29 PM2/8/15
to clo...@googlegroups.com
I missed the salient point about data transformations which is that of abstractions. In OO (Java at least) almost everything was a special case, in Clojure it is the polar opposite; almost nothing is a special case. 

It is astonishing how many domains can be sufficiently modeled as a sequence of maps [{..} {..} ...] and can be sufficiently transformed with the 'map' function (with assoc/assoc-in).
> 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 unsubscribe from this group and stop receiving emails from it, send an

Henrik Eneroth

unread,
Feb 9, 2015, 3:31:07 AM2/9/15
to clo...@googlegroups.com
If you find yourself passing the same argument over and over, you can always work in a partial:

(def save-account (partial save db))
> 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 unsubscribe from this group and stop receiving emails from it, send an

Gregg Williams

unread,
Feb 9, 2015, 1:10:26 PM2/9/15
to clo...@googlegroups.com
@Dru, I feel I'm ahead of you in learning Clojure, but I'm not yet to where @Colin is. However, I'm close enough that I recognize how accurate and concise his advice is--in fact, I'm saving it to remind myself!

Also, I just finished reading Functional Programming Patterns in Scala and Clojure, by Michael Bevilacqua-Linn (Pragmatic Programmer, publisher). In it, he takes standard OO patterns, gives Java examples, then shows how the same things are accomplished in functional style in Scala and Clojure. I found it to be very instructive.

A similar book by Brian Marick, a well-known Clojure programmer, is at https://leanpub.com/fp-oo. I haven't read it, but it looks promising.

Good luck on your learning!

-- Gregg

Dru Sellers

unread,
Feb 11, 2015, 10:19:44 AM2/11/15
to clo...@googlegroups.com
Thank you everyone for all of the helpful ideas.

I def like the Component library and I will have to sit down and figure out how to best work that into my application while it is still young.
All of the links have been consumed and I am starting to absorb more and more. Thank you for taking the time to share your thoughts and ideas with me.

-d

Herwig Hochleitner

unread,
Feb 11, 2015, 9:54:25 PM2/11/15
to clo...@googlegroups.com
Hm, the most common way to encapsulate in clojure is with a closure:

Your account repository would be:

(defn account-repository [connection]
  ;; <- here goes additional constructor logic
  (fn save [account]
    (sql-save connection account))

If the repository has more methods than just save, you can return a map of functions, or an implementation of a protocol / interface.

In my experience, component is most useful when talking to stateful APIs at the fringes of the application. There you need lifecycle methods to let the user (or system) nudge the application from one state to the next, observing and doing stuff along the edges. But the meat of an application (calculating what to do) can normally be stateless functions over data.

A recent alternative for when there are only two lifecycle events around a single updater method (construct, finish, step), are transducers.

hth, kind regards
Reply all
Reply to author
Forward
0 new messages