Home > clojure, programming, ruby > JRuby + Clojure’s Immutable Data Structures = Easy to maintain, application data-model.

JRuby + Clojure’s Immutable Data Structures = Easy to maintain, application data-model.

Implementing an application with rich data-model which can be updated by multiple UI controls, many concurrent threads with undo/redo functionality may be somewhat cumbersome. In order to ease this task, the functional programming paradigm with the immutable data structures turned out to be useful.

Because all good developers are lazy, one should seek for reuse rather than reinventing required tools, especially when there is good existing one. I tried to follow that path. Since we are using JRuby as our language of choice here at Trampoline, I decided to look more closely at clojure’s immutable data structures. It is straightforward to use Java classes from JRuby which is described in many places on the web already (here, here & here). The unknown to me was how can I use clojure’s objects from Jruby. Apparently clojure data structures are delivered as pre-compiled java classes and no runtime interpretation/compilation of clojure scripts is needed. The task turned out to be very easy.

The simple implementation of graph data structure with no deletion functionality looks as simple as:

The simple implementation of graph data structure with no deletion functionality looks as simple as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module Java::ClojureLang::IPersistentMap
  def keys
    keySet()
  end
  def has_key?(key)
    containsKey(key)
  end
  def include?(key)
    containsKey(key)
  end
  def key?(key)
    containsKey(key)
  end
  def member?(key)
    containsKey(key)
  end
end

In order to have Clojure collections look more like Ruby ones one can define aliases for their methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class BasicGraph
 
  include_package 'clojure.lang'
 
  attr_reader :nodes, :edges
 
  def self.empty
    BasicGraph.new(PersistentHashMap::EMPTY, PersistentHashMap::EMPTY)
  end
 
  def add_node(node, id)
    BasicGraph.new(@nodes.assoc(id, node), @edges)
  end
 
  def add_edge(source_id, target_id, edge)
    if (@nodes.containsKey(source_id) && @nodes.containsKey(target_id))
      BasicGraph.new(@nodes, @edges.assoc([source_id, target_id], edge))
    else
      self
    end
  end
 
 
  private
 
  attr_accessor :nodes, :edges
 
  def initialize(nodes, edges)
    @nodes = nodes
    @edges = edges
  end
end

Unfortunately (or fortunately due to different contract) we can not do it with all the methods. Particularly with mutating ones. That’s because Ruby’s = (assign operator) semantics is to return the value being assign. It is analogous to []= method as well. So even if we redefine the []=(key, val) method so that the method returns the updated version of the collection, the Ruby interpreter will step into the scene and wrap the whole method, so that it eventually returns val. Anyway, whether this is good or bad is the topic for a whole other post.

  1. June 15th, 2009 at 07:48 | #1

    Hi, interest post. I’ll write you later about few questions!

  2. July 6th, 2009 at 15:57 | #2

    I’m glad that after surfing the web for uch a long time I have found out this information. I’m really lucky.

  1. No trackbacks yet.