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

Reply via email to