I have no idea what, if anything, Clojure the language(s) *should* do about this issue, but I can explain the reasoning behind CLJX.
CLJX uses the reader to statically analyze toplevel forms in a file and generate new ones according to declarative rewrite rules. The motivating use case was to make the C2 data visualization library work on both Clojure and ClojureScript. C2's source is spit across src/cljx ;;most of the code src/clj ;;Clojure-specific src/cljs ;;ClojureScript-specific (i.e., DOM manipulation code) and most of the cljx files only have one or two platform metadata annotations. Any namespace which uses macros has two ns forms to accomidate ClojureScript's :use-macros syntax. I explicitly wanted a solution where code for different platforms would be in the same file because it eases the burden on the maintainer to ensure consistency between implementations. That makes sense when the differing implementations are very small: (defn ^:clj log10 [x] (Math/log10 x)) (defn ^:cljs log10 [x] (/ (.log js/Math x) (.-LN10 js/Math))) and I agree with Dave in that it could get unwieldy fast if you try to mix larger implementation-specific functions together. If I wanted to write something cross-platform, I'd start by defining the needed foundation (beyond what is provided by the Clojure runtimes, e.g., "str"), write those platform-specific functions completely separate from each other, and then write the shared Clojure code that implements the bulk of the functionality. The CLJX solution works nicely with C2 because 90% of C2 code manipulates Clojure data structures only. There are relatively few implementation details that need to be papered over. Clojure was designed to be a hosted language, and it's very easy to reach out to the underlying platform. If you don't need to do that and can get by with Clojure/core---great, your code will be portable. However, if you use libraries and facilites native to the host platform, then you're not writing Clojure so much as you are writing Clojure+Java or Clojure+JavaScript. If that's the case, just have separate projects with a shared spec or test suite and not worry about mashing it all together with compiler or pre-processing tricks. On Apr 13, 10:06 am, Aaron <aaroncrael...@gmail.com> wrote: > I posed this question briefly in this post here: > https://groups.google.com/d/msg/clojure/K65Va0rCCls/Ow5bAJ_YTGIJ. In this > real world example of porting Korma over to ClojureCLR, I encountered the > following things: > > - One namespace which handled JDBC interaction needed to be rewritten > completely to target ADO.NET > - Another namespace required a few minor changes, only one of these > (changing .indexOf to IndexOf) was platform specific > > Korma is a library that is probably exceptional in its avoidance of > platform specific features in most namespaces. Porting something like > swank would not have been so easy. In general, I would say that for this > library, I would prefer to use the platform specific folder approach - i.e > having two namespaces korma.platform.db and korma.platform.util in src-jvm/ > and src-clr/ folders. Then there could be a CLR specific project.clj > (maybe project.clr.clj) sitting next to the JVM project.clj (and eventually > maybe a project.py.clj). Each project.clj would reference the correct > platform specific folder in addition to the shared src/ folder. Just off > the top of my head, the reasons for supporting this approach are: > > - It forces the developer to isolate platform specific code into > manageable units that can be 1) more easily ported to another platform, 2) > easily maintained, and 3) tested in isolation for compatibility > - There is no need for one clojure dialect to know about the whereabouts > or contents of code for another dialect. This would seem to be unnecessary > overhead. > > While I can see why it might be nice to be able to quickly add some > metadata to distinguish this platform specific code while keeping > everything in one file, as other have mentioned, I would encourage people > to think about the downsides of this. Say, I had just taking korma.db and > put some conditionally compiled CLR code alongside the JVM code. The file > doubles in size. Would I then want the py team to do the same? Also, for > the one .indexOf -> IndexOf change, say there are little changes like that > all over the place. Would a team adding a new port want to go through the > whole source tree and find every instance of this? Without modifying the > clojure compilers we have now, the platform specific directory approach > will work and is probably cleaner. It just requires team consensus on > adopting this approach. > > One thing, I would propose is adding some build tool awareness of this so > that maybe an NLein or PyLein could use the same project.clj and just see > :platform-clr, :platform-py, :platform-jvm attributes. Or this could be > done by having a shared project.shared.clj included by each platform > specific project file. These types of approaches might be simpler and more > maintainable in the long run. > > Aaron > > > > > > > > On Thursday, April 12, 2012 12:56:59 PM UTC-4, tbc++ wrote: > > > I've been thinking lately how to seamlessly merge clojure-py and > > clojure-jvm code in the same packages. This is something I know has > > been discussed in the past, but I'm just looking for > > ideas/brainstorming on how we can solve this problem. > > > Let's begin by explaining the problem. Let's assume that my project > > needs a platform specific way to convert a int to a string: > > > On clojure-jvm: > > > (defn to-string [i] (.toString Integer i)) > > > On clojure-py: > > > (defn to-string [i] (py/str i)) > > > On Clojurescript: > > > (defn to-string [i] (.toString i)) > > > We could do this inline (inside a common .clj file): > > > (for-platform "jvm" > > (defn to-string [i] (.toString Integer i)) > > "python" > > (defn to-string [i] (py/str i)) > > "js" > > (defn to-string [i] (.toString i))) > > > But this gets ugly real fast. > > > My idea, would be to provide some sort of hook to the compiler, that > > would be cross-platform. The compiler, when looking for a namespace, > > would find all files in a given folder that match the required > > namespace: > > > /test/to_string.clj > > /test/to_string.cljpy > > /test/to_string.cljs > > /test/to_string.cljclr > > > These function hooks would then pick the best entry based on the > > extension, defaulting to .clj if no better option exists. Since these > > functions could be composable it would be possible to also dispatch on > > platform versions: > > > /test/to_string.cljpy26 ; Python 2.6 code > > /test/to_string.clj7 ; JVM 7 code > > > It seems to me that this would be a very clean way to allow clojure > > programs to be cross-platform. The side-effect is that it would force > > non-standard code out into separate namespaces where they can be > > easily maintained. If someone wanted to port the project to a new > > platform, he would need only to find existing platform overrides, and > > create new entries. > > > Thoughts? > > > Timothy Baldridge -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en