On Jan 12, 5:56 am, Daniel Werner <daniel.d.wer...@googlemail.com> wrote: > On Jan 11, 4:20 pm, Ram Krishnan <kriyat...@gmail.com> wrote: > > > > * Mozilla's JS 1.7 supports a let statement[1] with lexical scoping, > > > ... > > That's an interesting idea, although I'm not too keen on specializing > > for any one browser. The other problem is I don't see any reasonable > > way of providing the alternates from a web server without some form of > > user-agent sniffing. > > You're right, any potential benefits could only be reaped under very > specific circumstances. Making the codebase more complex to optimize > for these limited cases doesn't make sense. > > I've taken a deeper look yesterday and am positively surprised how > much easier to understand the implementation is than expected. Very > cool. A few design decisions caught my eye though, and could IMO be > improved:
Glad you found the code readable, which is always a concern with Lisp code that has evolved over the course of its author discovering the problem he's setting out to solve :) > > Using a ref won't actually improve your concurrency experience with > *macros* (provided I understand your code correctly). Since the > parsing and emitting is written in an inherently linear style, there > will only ever be one transaction altering *macros*. More problematic > though is the case where two threads happen to concurrently translate > completely independent (js ...) expressions. Since *macros* is global, > both threads would share the same macro definitions even if totally > different code bases are being translated! This problem could be > mitigated by using thread local bindings for *macros*. Refs or atoms > are not neccessary in this case; even plain old (set!) would do. I agree. Funnily enough, I started out with a thread local binding implementation, and switched to a ref because I needed macro definitions to persist across multiple requests. Consider something like the following: (defroutes my-routes (GET "/js/app.js" {:content-type "text/javascript" :body (tojs (resources-path "boot.cljs") (resources-path "app.cljs"))}) (GET "/login" login-form-handler)) /js/app.js would load and compile the boot.cljs and app.cljs scripts and return the resulting Javascript in one request. If all the Javascript were done this way, there would be no issue with just thread local bindings for *macros*. However, if you had some inline Javascript elsewhere in a different request handler: (defun login-form-handler [] (let [id (gensym)] (html [:div {:id id :title "login"} [:form {:id "login-form"} (text-field "login-email" "email") (password-field "login-password" "password")] [:p {:id "login-message" :class "message error"} ""] (jq-let [dlg-id (str "#" id)] (defn login [] (.text ($ "#login-message") "login submit")) (defn do-close [] (.dialog ($ this) "close")) (.dialog ($ dlg-id) {:autoOpen false :modal true :height 230 :buttons {"login" login "close" do-close}}))]))) The issue is that the clojurejs script within the `jq-let' wouldn't see any of the macros unless they're loaded again, which can get expensive on each request. Ideally, the macro definitions would persist across requests, but in independent namespaces to avoid the collision issue you brought up. I'd like to think a cheap namespace implementation would be a way around this (basically, macro expanders would be kept in a global *namespaces* map with the namespace name as key). This would have to introduce a `ns' top level form, with at least support for :use. Or I'm open to other suggestions ... > > Another thing that strikes me as a potentially bad idea is the > reliance on imperative behaviour to generate output, i.e. "emitting" > or "printing" generated code to a stream instead of returning the > pieces in a functional way and combining them afterwards. This > imperative style could hamper your code's composability in the > future. ... What do others think? I completely agree. This is one of the bad side-effects (no pun intended) of the ad-hoc origin of this library. Ideally, there should be an intermediate representation which captures all of the Javascript idiosyncracies, and a much simpler emitter. I made the mistake of assuming the s-expression parse tree *was* that representation ... live and learn. The other factor is that I'm a Common Lisp hack still getting used to writing idiomatic Clojure :) > With all that said -- it's actually quite pleasant to work with right > now. If you're interested in patches, I've done some small > refactorings to remove duplicate code, added basic docstring support > and other small fixes. Please see my fork at: > > https://github.com/danwerner/clojurejs > > Daniel Thanks very much. I'm glad you were able to use and improve the code. I like the refactoring you did, as well as the changes to be more Clojure 1.2. I have to admit, I'm still coming up to speed on 1.2, so that is most appreciated. The docstring feature will help anyone trying to migrate server side Clojure code to the browser. I haven't done this before but I believe the way github recommends merging patches across repos is via a `pull request' [1]. Also, let me know if you'd like to have write access to the clojurejs repo that I setup. I'd be happy to add you as a collaborator. [1] http://help.github.com/pull-requests/ Cheers, -ram -- 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