Saturday, September 24, 2011

Clojure's PersistentHashMap on JRuby

Clojure is an impressive language. Not just succinct syntax, Clojure has immutable data types for concurrency. Such Clojure's persistent data types might be useful in some cases in other languages. A question is whether we can use Clojure data types from JRuby. The answer is yes. Below is what I tried on irb:

$ rvm jruby-1.6.4
$ irb
jruby-1.6.4 :001 > require 'java'
=> true
jruby-1.6.4 :002 > $CLASSPATH << "/Users/yoko/Tools/clojure-1.3.0"
=> ["file:/Users/yoko/Tools/clojure-1.3.0/"]
jruby-1.6.4 :003 > require 'clojure-1.3.0-slim'
=> true
jruby-1.6.4 :004 > h = {"a" => 100, "b" => 300, "c" => 200}
=> {"a"=>100, "b"=>300, "c"=>200}
jruby-1.6.4 :005 > pmap = Java::clojure.lang.PersistentHashMap.create(h)
=> {"a"=>100, "b"=>300, "c"=>200}
jruby-1.6.4 :006 > pmap.class
=> Java::ClojureLang::PersistentHashMap

OK. I could successfully create Clojure's PersistentHashMap object. PersistentHashMap implements java.util.Map interface, which means the object has all of Ruby's Hash methods.

jruby-1.6.4 :007 > pmap.each {|k, v| puts "#{k} is #{v}"}
a is 100
b is 300
c is 200
=> {"a"=>100, "b"=>300, "c"=>200}
jruby-1.6.4 :008 > pmap.select {|k, v| k > "a"}
=> [["b", 300], ["c", 200]]
jruby-1.6.4 :009 > pmap.has_value?(400)
=> false

PersistenHasMap is immutable. So, when the method tries to change its data, Clojure raises exception:

jruby-1.6.4 :012 > pmap.delete_if {|k, v| k >= "b"}
Java::JavaLang::UnsupportedOperationException:
from clojure.lang.APersistentMap.remove(APersistentMap.java:273)
from org.jruby.java.proxies.MapJavaProxy$RubyHashMap.internalDelete(MapJavaProxy.java:157)
from org.jruby.RubyHash.delete(RubyHash.java:1407)
(snip)

This way, we can use Clojure's immutable data types on JRuby.

4 comments:

Tim said...

For this to be interesting, you'd need Clojure concurrency primitives like dosync and send, right?

yokolet said...

Yes. People are often concerned about thread safety. Clojure's idea on concurrency might work well on JRuby, too.

dr said...

Cool. For what it's worth, I recently started an experiment that exposes most of Clojure (no macros) to JRuby. Here it is. It will probably need a lot of work, especially performance, for real work, but it's kind of fun.

yokolet said...

dr: Wow, that's a nice Ruby wrapper. I think there's a way of implementing by JRuby extension. You can mix simple Ruby wrapper and extension methods.