Change #1) There should be a separate :loop operationProblem: loops are :let operations with :is-loop true. This came about because the implementation of emit for let and loops are similar, but for other types of AST passes loops are much more similar to functions than to lets. It's completely arbitrary that :loop is the only special form that doesn't have it's own :op.Solution: Split up the operations.Breaking change with a very simple patch: https://github.com/brandonbloom/clojurescript/commit/22ca802d53eb6f77526773815d801be164e9c530
Change #2) All blocks should expand to a :do nodeProblem 2A) A bunch of redundancy happens whenever a special form allows multiple statements in a block. Analyze has the 'analyze-block helper and emit has the 'emit-block helper. Having written two other AST passes (Abstract Normal Form and Continuation Passing Style), I had to create anf-block and cps-block helpers as well. This redundancy leeks out into every single pass by necessity.Problem 2B) When a block is empty, there is no AST node on which to hang the :env property. When performing an AST transformation, you sometimes need the env at the end of a block, even if the block is empty.Solutions: Replace ~@exprs with (do ~@exprs) wherever a special form employs an implicit block.This is another breaking change with a straightforward patch: https://github.com/brandonbloom/clojurescript/compare/distinct-let...implicit-do
Who currently depends on the shape of the AST?
What is the process for making breaking changes to the AST?
Do these changes make sense?I've started a design page to capture some additional notes regarding the AST: http://dev.clojure.org/display/design/Formalize+AST
Could you elaborate on where :loop's are like :fn's? They look more like :let's to me, with a recur point.We seem to be supporting combinations of these features:1. Scope local bindings2. Provide inits to local bindings3. Make a recur point4. Create a fn:let has 1,2:fn has 1,3,4:loop has 1,2,3
1. Interop - CLJS has mainly :js* and :dot, Clojure has many interop nodes, some essential, others for optimisations. This is undoubtedly the biggest difference between Clojure implementations.2. defrecord - CLJS has a :defrecord node, Clojure doesn't
3. local binding "init" nodes are slightly simpler in CLJS
4. Clojure has vars, thus :the-var AST node.
5. Some small differences with try/catch which I don't fully understand.
Again, thank you Brandon for your great work.
Could you elaborate on where :loop's are like :fn's? They look more like :let's to me, with a recur point.
We seem to be supporting combinations of these features:1. Scope local bindings2. Provide inits to local bindings3. Make a recur point4. Create a fn:let has 1,2:fn has 1,3,4:loop has 1,2,3That's a good way to look at it. One thing worth noting is that all of these really are the same thing: lambda abstractions.Consider the loop:(loop [x 3] (when (pos? x) (println x) (recur (dec x))))It's actually equivalent to:((fn [x] (when (pos? x) (println x) (recur (dec x)))) 3)
Similarly, you could say that:(let [x 1] (inc x))Is the same as:((fn [x] (inc x)) 1)You know, lambda calculus and all that...
The interesting bit for me is that recur implies a tail call and so when transforming into continuation passing style, I can apply the above trivial transformation and then delegate to the :fn implementation for the recur trampoline. As a result, :loop looks like a :fn with an initial :invoke, instead of looking like a :let.We don't have any other bi-modal AST nodes, so it seems like a good idea to enumerate all the possible states as true node ops.
1. Interop - CLJS has mainly :js* and :dot, Clojure has many interop nodes, some essential, others for optimisations. This is undoubtedly the biggest difference between Clojure implementations.2. defrecord - CLJS has a :defrecord node, Clojure doesn'tI don't think there's anything wrong with having interop nodes, but I'd like to ultimately segregate them. Both compilers currently pipeline muddles together parsing, macro expansion, higher level analysis, platform concerns, optimizations, etc.
It would be nice to have the analyzer made completely agnostic of the target platform in the earliest stage: before macro expansion. The AST before macro expansion would be useful for codeq to find usages of macros, etc. This would enable target-agnostic analysis to occur without having to ignore target-specific nodes.
3. local binding "init" nodes are slightly simpler in CLJSI have some ideas about further improving this, but let's focus on :loop and implict :do right now :-)
5. Some small differences with try/catch which I don't fully understand.JavaScript's catch does not provide conditional catching by type like Java does. There is a 'try macro which expands to the 'try* form. The 'try* form uses a 'cond to simulate type filtering.
Again, thank you Brandon for your great work.My pleasure. I'd really love to see Typed-Clojure working on a common AST for both Clojure and ClojureScript!
So I assume this is doable without having a separate :loop node: :loop would just be more convenient for some users of analysis.Could this change mean more work for some common usages of analysis? Possibly yes for some very simple analysis of the AST, but it seems like a welcome change more complicated libraries like CPS.For Typed Clojure: handling of :let and :loop are almost identical, except for adding a recur point.
Great idea.
Perhaps add it to the design page for the meantime.
Ah, got it (even though you just explained it to me on IRC). Any issues here? Does CLJS/CLJ need to change here?
The main benefit here is having 1-to-1 special forms with op keywords, which is mostly a cosmetic change.
--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.To post to this group, send email to cloju...@googlegroups.com.
To unsubscribe from this group, send email to clojure-dev...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/clojure-dev?hl=en.
Ambrose: I was thinking about starting a branch to document the different AST nodes with typed-clojure. Do you think that's feasible?
--
--
You received this message because you are subscribed to the Google Groups "Clojure Dev" group.
To view this discussion on the web visit https://groups.google.com/d/msg/clojure-dev/-/7v5IcvGzTigJ.