I Just need some mechanism like “methondMissing” in Ruby. When a nonexistent var is called, the lookup system will try to call a “-var-missing” function in the namespace. This function should return a var and clojure compiler assumes this var as the one it was looking for.
For example, in the shake library:
(defn -var-missing [sym]
To support lazy loading, shake 0.3.0 won’t read your path. It will now create vars on demand. So on calling sh/uname, a var named `uname` will be created. And to create a var, just use intern or eval.
(require '[shake.core :as sh])
I’m sorry there is no way to implement this except hacking into Clojure’s compiler. Fortunately, it’s not too difficult to find out the injection point.
As you may know, there are two phases in Clojure compiler: expanding macros and evaluating forms. Both phases will look up vars to find macros or values. So we should take care both of them.
All code diff is here:
Adding -var-missing is just an attempt to implement lazy loading of vars. And it provides another smooth syntax for writing DSLs. But actually, Clojure’s macro system provides a great metaprogramming mechanism. So in most case, you don’t have to hack into the Compiler like this. Just put your DSL into a top level macro, and you can get them done all in clojure scripts.
As shake goes public, I received a lot of feedback. The top issue is about using clojure variables in shake macros. Now it has been fixed in 0.2.2. Let me show you the new syntax.
Using vars, local bindings in shake macros:
(require '[shake.core :as sh])
(let [x "/home/nsun"]
(sh/ls -l $x))
So you have to prefix the clojure variable with a dollar sign. This is quite similar to what we did in shell programming.
And more interesting, you can also use a $ prefixed clojure form in shake:
(sh/curl $(format "https://github.com/%s" "sunng87"))
Thanks to Clojure macro system, it has great flexibility to manipulate symbols and code lists, making inventing new syntax much easier than other languages. Shake can be a great example in describing macro system.
You might have heard of sh, which brings python an interface to call subprocesses. The API of sh is pretty cool: Every command can be treated as a python function, and imported from a namespace. Options and arguments are passed in as python string.
But I think in Clojure, things can be even cooler. We dynamically create symbols for every program. We will have a beautiful DSL so you don’t have to quote arguments as string. So when you are using this library, it may look like:
(ip -4 addr)
And actually it’s just like that! I create this library called shake. When you load `shake.core`, it indexes all the executables in your path. Then all programs are available to you in a clojure native way.
(uname -a) ;; returns a java.lang.Process, that you can send data, read data and wait for termination.
;; for those just need output
(alter-var-root *print-outpt* (fn [_] true))
;; it prints ...
There’s a lot of fun in implementing this library. First, to be able to use custom symbol in the DSL, you have to make these executables as macros. Second, find a way to programmably create vars which are named by string. The power of Clojure enables all the ideas and makes it possible. Check out the source code if you are interested in: https://github.com/sunng87/shake/.
I almost forgot to announce this library I made half of a year ago. This library is aiming to bring Clojure to GIS. So you can manipulate geometry objects with a set of clojure functions.
The library covers :
- Geometries defined in Simple Feature Spec
- Spatial relationship test, based on DE-9IM.
- IO functions, WKT and WKB support
- some spatial analysis functions such as buffer, convex-hull
Also, this week Alexey Pushkin sent pull request and added support for Perpared Geometry and Affine transformations.
The current release of cljts is 0.2.0-SNAPSHOT. You can find API document at here.
If you are also interested in bringing clojure to GIS, feel free to get connected and hope I could help you.