a clojure question

4 views
Skip to first unread message

Raoul Duke

unread,
Aug 27, 2015, 3:45:59 PM8/27/15
to AgileWikiDevelopers
Pretty please if you feel like you have the luxury, explain in English
and/or pseudocode how trying to do things in an immutable way works
out. Especially how it interacts with OO. Or point out some other
texts online you've seen that sum it up usefully.

I've read and even done some Clojure (i was into clojure before it was
cool - seriously :-) but haven't had the time and energy to follow
through on it. So while I can grok what it might be like in an FP
Haskell purity type setting to do things via monads and do notation
etc., when I try to start with my own OO project and move it towards
immutability, my small brain easily gets freaked out as I'm pulling at
the strand of yarn off of the sweater.

William la Forge

unread,
Aug 28, 2015, 9:07:09 AM8/28/15
to AgileWikiDevelopers
As one small brain to another I can give you answers that work for me.

First, as to why immutability. It is important to understand every possible advantage as you pay such a high price for it. It allows you to reason about your code even when doing multi-threading.

Not everything is immutable though. Things that are not though often come with different types of guards to ensure thread safety. Like thread-local variables, atoms, agents and refs. Also, when you need to, you can turn immutability off for most immutable structures for a major performance gain. But you gotta make sure that the access scope is then severely limited or it will get tough to reason about your code.

To survive immutability, you do a lot with sequences. Much of the Clojure API is about sequences. And sequences can be lazy. Like scala, lazy is encouraged but often means there is a sync lock under the covers. :0

Objects are discouraged, as they are hard to write the code for, maintain immutability and performance, and get anything useful done. Records are recommended, which are app-optimized maps with lots of implicit code to help with immutability. 

Strangely, iterators are used under the covers. These are used to turn structures into sequences. They implement the Java Iterator interface, and are fully mutable. And yeah, they are real objects. You can even write them in Clojure, though most are written in Java. (Clojure is not yet written in Clojure, but there are projects in progress to do that.)

Below I've included my tree map iterator and sequencer. Early versions. And not typical code at all. Both use deftype rather than defrecord, giving you greater speed but leaving immutability in your own hands. And the iterator is NOT immutable!!! I also see a bug in the sequencer, but it is also present in the Java code I am translating into Clojure and that Java code is part of the implementation of Clojure. :D (OK, it is a pretty small bug.)

Note that Clojure supports all aspects of OO, for full bi-directional interoperability with Java. But it discourages inheritance most strongly. Though you CAN do subclassing with gen-class. And it can be pretty awkward at times.

Let me conclude that Clojure is a very idiomatic language that adds a lot of data structures and sequence functions to lisp. As well as a lit of weird things to make Java interoperability possible. It is quite dense, and I think it could eventually be fun to use, though I am not there yet by a long shot!

Bill



(ns aa-collections.immutable.map-iterator
(:require [aa-collections.immutable.map-node :refer :all])
(:import (clojure.lang Counted IMapEntry)
(java.util Iterator)
(aa_collections.immutable.imap_node IMapNode)
(aa_collections.immutable MapSequence)))

(deftype map-iterator [^IMapNode node
^{:volatile-mutable true IMapEntry true} lst
^{:volatile-mutable true int true} cnt]
Iterator
(hasNext [this]
(> cnt 0))
(next [this]
(if (nil? lst)
(set! lst (first-t2 node))
(set! lst (.next-t2 node (.getKey lst))))
(set! cnt (- cnt 1))
lst)

Counted
(count [this] cnt))

(defn new-map-iterator [node]
(->map-iterator node nil (.-cnt node)))

(defn ^MapSequence new-map-seq [^IMapNode node]
(MapSequence/create (->map-iterator node nil (.-cnt node))))


(ns aa-collections.immutable.MapSequence
(:gen-class
:main false
:extends clojure.lang.ASeq
:implements [clojure.lang.Counted]
:constructors {[java.util.Iterator]
[]
[clojure.lang.IPersistentMap Object]
[clojure.lang.IPersistentMap]}
:init init
:state state
:methods [^:static [create [java.util.Iterator] Object]]
:exposes-methods {count superCount})
(:import (aa_collections.immutable MapSequence)))

(defn -create [iter]
(if (.hasNext iter)
(new MapSequence iter)
nil))

(deftype seq-state [iter val rst])

(defn -init
([iter]
(let [s (->seq-state iter (atom nil) (atom nil))]
(reset! (.-val s) s)
(reset! (.-rst s) s)
[[] s]))
([meta s]
[[meta] s])
)

(defn -withMeta [this meta] (new MapSequence meta (.-state this)))

(defn -first [this]
(let [s (.-state this)
v (.-val s)]
(if (= s @v)
(swap! v #(if (= s %) (.next (.-iter s)))))
@(.-val s)))

(defn -next [this]
(let [s (.-state this)
r (.-rst s)]
(when (= s @r)
(-first this)
(swap! r #(if (= s %) (-create (.-iter s)))))
@(.-rst s)))

(defn -count [this]
(let [iter (.iter (.-state this))]
(if (counted? iter)
(.count iter)
(.superCount this))))



--
You received this message because you are subscribed to the Google Groups "AgileWikiDevelopers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to agilewikidevelo...@googlegroups.com.
To post to this group, send email to agilewiki...@googlegroups.com.
Visit this group at http://groups.google.com/group/agilewikidevelopers.
For more options, visit https://groups.google.com/d/optout.

Raoul Duke

unread,
Aug 28, 2015, 11:53:11 AM8/28/15
to AgileWikiDevelopers
Thanks! I can understand a "turn based" immutability like with agents
where the entire state gets swapped out. That is like how one writes a
video game in a functional-immutable style: each frame changes the
whole world out, the old one is discarded. (Modulo efficiencies of
persistent data structures sharing things.) I haven't ever gotten my
head around any other way e.g. FRP yet.

William la Forge

unread,
Aug 28, 2015, 2:45:24 PM8/28/15
to AgileWikiDevelopers
Took a quick look at frp. Looks a lot like eventual consistency to me!

But as for the rest, the whole state does not get swapped out, only a reference to a composite built from immutables which often get reused in subsequent states. So if your state is a binary tree, changing a leaf value means you are replacing log n nodes rather than n nodes. (And the same thing happens when updating my incrementally deserialized data structures.)

Raoul Duke

unread,
Aug 28, 2015, 3:46:29 PM8/28/15
to AgileWikiDevelopers
Ja, the sharing I'm aware of, & thankfully it is all under the covers,
so that is nice! It is mostly trying to get my head around the idea of
how the immutability might percolate up. :)

William la Forge

unread,
Aug 28, 2015, 4:19:12 PM8/28/15
to AgileWikiDevelopers
I've not seen anything that would indicate that it can percolate up. For example, the entire state of the Datomic database is held by a single reference.

In general, when updating a composition of immutable structures, I always do it via a method on the top data structure. Clojure does this too with assoc-in: https://clojuredocs.org/clojure.core/assoc-in

Raoul Duke

unread,
Aug 28, 2015, 4:21:00 PM8/28/15
to AgileWikiDevelopers
> In general, when updating a composition of immutable structures, I always do
> it via a method on the top data structure.

Right. The reductio ad absurdum question is, where is that top level?
In Haskell it would be the root Main(), not just a variable in there.
:-) Each language / approach has their own philosophy on how to do it,
each with pros/cons.

William la Forge

unread,
Aug 28, 2015, 4:46:02 PM8/28/15
to AgileWikiDevelopers
Actually, we are forgetting about STM, which allows you to safely update more than one "root". :-)

Raoul Duke

unread,
Aug 28, 2015, 4:50:34 PM8/28/15
to AgileWikiDevelopers
yeah, i've been interested in STM for a long time. I was a fan boy.
But then I also briefly met Cliff Click through work and followed his
musings for a while online, and these days i have a middle-road
attitude about it all. So STM is cool but not a super perfect silver
bullet.

William la Forge

unread,
Aug 28, 2015, 6:27:47 PM8/28/15
to AgileWikiDevelopers
Yup! Usually I need durability when I implement transactions.

Reply all
Reply to author
Forward
0 new messages