Writing programs that target Clojure and ClojureScript involves a lot of copy and pasting. The usual approach is to copy the whole code of one implementation to a source file of the other implementation and to modify the platform dependent forms until they work on the other platform. Depending on the kind of program the platform specific code is often a fraction of the code that works on both platforms. A change to platform independent code requires a modification of two source files that have to be kept in sync. To solve this problem branching by target platform on a form level would help a lot.
See also ticket CLJS-27.
Tools like lein-cljsbuild provide some help using "crossover" files. Crossover files are usually written in Clojure and contain only code that works on both platforms. On compilation the crossover files are run through a preprocessor that removes the special comment ;*CLJSBUILD-REMOVE*; from the source file to allow platform specific references to macros. This is explained in detail over here:
As mentioned above the granularity where this solution solves the problem is probably not at the right level. Having only one function with a platform specific form puts one into the business of splitting it into two files maintaining two different versions of the function. A branching granularity on a form level would probably be much better.
A solution to this problem that is used by Common Lisp implementations are feature expressions. Each platform has a variable called
*features* that contains keywords that indicate the supported features of the platform the code is running under. The branching on a platform or a platform specific feature is done via the reader macros
#- followed by a feature condition. The feature condition is either a symbol or a form that combines symbols with the
not operators. The feature condition is evaluated by looking up the symbols in the
*features* variable. If the feature condition evaluates to true the next form will be passed through the reader and evaluated, otherwise it will be discarded.
(ns feature.expressions #+clojurescript (:require [goog.string :as gstring])) (defn my-trim [s] #+clojure (.. s toString trim) #+clojurescript (gstring/trim s)) (my-trim " Hello CL? ")
The patches add a dynamic variable called
*features* to the clojure.core and cljs.core namespaces, that should contain the supported features of the platform in question as keywords. Unlike in Common Lisp, the variable is a Clojure set and not a list. In Clojure the set contains at the moment the :clojure keyword, and in ClojureScript the :clojurescript keyword.
I would like to get feedback on the following issues:
- Are those keywords ok? Is :jvm for Clojure and :js for ClojureScript better?
- Should ClojureScript add something like :rhino, :v8 or :browser as well?
- Someone mentioned that this should support Clojure's namespaces. How should this be done?
- How should the compilation process work with this extension?
- Will the ClojureScript compiler read
*.cljfiles? What happens to
To run the ClojureScript tests, drop a JAR named "clojure.jar" that has the Clojure patch applied into ClojureScript's lib directory.
The Common Lisp Hyperspec about the Sharp Sign macros: