Skip to content

Instantly share code, notes, and snippets.

@matlux
Last active January 23, 2017 11:35
Show Gist options
  • Save matlux/f31fde87e58867d7a182 to your computer and use it in GitHub Desktop.
Save matlux/f31fde87e58867d7a182 to your computer and use it in GitHub Desktop.
Clojure and Scala are both amazing functional languages that have a lot in common. For example their immutable data structures. Both have some amazing libraries. Why not being able to call one from the other? This is a simple Clojure in Scala interop. This is an example showing how to call Clojure function within Scala using standard Scala data …
sealed trait EdnExpr {
def value : Expr
def mdata : Map[Expr,Expr]
override def equals(other : Any) = other match {
case that : EdnExpr => this.value == that.value
case _ => false
}
}
type Expr = Any
//EdnNumber,EdnString,EdnBoolean,EdnMap, etc... could be derived from this to add meta data
//Let's just implement the missing Keyword and Symbol types for now.
case class EdnKeyword(ns: String,name : String,meta: Map[Expr,Expr]) extends EdnExpr {
def mdata = meta
override def value = toString
def getName = name
override def toString = {
if (ns == "" || ns == null) ":"+name
else ":"+ns +"/"+name
}
}
case class EdnSymbol(ns: String,name : String,meta: Map[Expr,Expr]) extends EdnExpr {
def mdata = meta
override def value = toString
override
def toString = {
if (ns == "" || ns == null) name
else ns +"/"+name
}
}
// converts EDN string into a Scala data structure
// returns Scala Map, Set, Vector, etc...
def readEdnString(str: String): Expr = {
val javaEdn = RT.readString(str)
java2scalaRec(javaEdn)
}
// converts a Scala data structure into an EDN string
def writeEdnString(expr: Expr):String = {
val prStr : clojure.lang.IFn = clojure.java.api.Clojure.`var`("clojure.core","pr-str")
prStr.invoke(scala2JavaRec(expr)).asInstanceOf[String]
}
//convert java Map,Set,Vector into Scala equivalent
// convertion is deep/recursive.
def java2scalaRec(expr: Expr): Any = {
expr match {
case _ : clojure.lang.IPersistentVector => expr.asInstanceOf[java.util.List[Expr]].asScala.toVector.map(subexp => java2scalaRec(subexp))
case _ : clojure.lang.IPersistentList => expr.asInstanceOf[java.util.List[Expr]].asScala.toList.map(subexp => java2scalaRec(subexp))
case _ : clojure.lang.IPersistentMap => expr.asInstanceOf[java.util.Map[Expr,Expr]].asScala.toMap.map{case (k,v) => (java2scalaRec(k),java2scalaRec(v))}
case _ : clojure.lang.IPersistentSet => expr.asInstanceOf[java.util.Set[Expr]].asScala.toSet.asInstanceOf[Set[Expr]].map(subexp => java2scalaRec(subexp))
case _ : java.util.List[Expr @unchecked] => expr.asInstanceOf[java.util.List[Expr]].asScala.toList.map(subexp => java2scalaRec(subexp))
case n: Number => n
case s: String => s
case b: Boolean => b
case null => null
case kw : clojure.lang.Keyword => EdnKeyword(kw.getNamespace,kw.getName())
case sym : clojure.lang.Symbol => EdnSymbol(sym.getNamespace,sym.getName())
case _ => throw new Exception("expr=" + expr + " (" + expr.getClass + ") is not Iterable")
}
}
//same as previous but the otherway around
def scala2JavaRec(expr: Expr): Any = {
expr match {
case EdnKeyword(ns,name,_) => clojure.lang.Keyword.intern(ns,name)
case EdnSymbol(ns,name,_) => clojure.lang.Symbol.create(ns,name)
case m : Map[Expr @unchecked,Expr @unchecked] => PersistentHashMap.create(m.map{case (k,v) => (scala2JavaRec(k),scala2JavaRec(v))}.asJava)
case s : Set[Expr @unchecked] => PersistentHashSet.create(s.map(subexp => scala2JavaRec(subexp)).toList.asJava)
case v : Vector[Expr @unchecked] => clojure.lang.PersistentVector.create(v.map(subexp => scala2JavaRec(subexp)).asJava)
case l : List[Expr @unchecked] => clojure.lang.PersistentList.create(l.map(subexp => scala2JavaRec(subexp)).asJava)
case _ => expr
}
}
// this is the binding of the the "pprint" Clojure function in Scala
val pprint = {
import clojure.java.api.Clojure._
val REQUIRE = `var`("clojure.core","require")
REQUIRE.invoke(read("clojure.pprint"))
`var`("clojure.pprint","pprint")
}
// Finally:
// this is the example of how to call the Clojure pprint function in Scala
def prettyPrintScalaDataStructureAsEdn(data: Any): String = {
val writer = new StringWriter()
pprint.invoke(scala2JavaRec(data),writer) // Calling Clojure function "pprint"
writer.toString
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment