This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # aleph (15)
- # announcements (4)
- # aws (2)
- # bangalore-clj (7)
- # beginners (236)
- # calva (24)
- # cider (11)
- # cljs-dev (63)
- # clojure (141)
- # clojure-europe (3)
- # clojure-india (2)
- # clojure-italy (8)
- # clojure-nl (3)
- # clojure-spec (8)
- # clojure-uk (52)
- # clojured (1)
- # clojuredesign-podcast (4)
- # clojurescript (35)
- # clojutre (3)
- # community-development (1)
- # cursive (77)
- # data-science (1)
- # datomic (3)
- # emacs (13)
- # fulcro (7)
- # graalvm (78)
- # graphql (2)
- # nrepl (7)
- # off-topic (18)
- # pathom (25)
- # reagent (12)
- # reitit (31)
- # shadow-cljs (178)
- # spacemacs (7)
- # tools-deps (32)
- # xtdb (10)
- # yada (3)
Hi All - I have a question about the implementation of LazySeq, actually several questions, but I'll stick with the main one first:
Since the lazy-seq macro expands into a LazySeq deftype (which wraps the body in a thunk) at macro-expand time, it is not until runtime afaict when
rest is called on the LazySeq that the the body is evaluated. However, when it is evaluated, the recursive nature of the body results in another call to lazy-seq, but this is after compile / macro-expansion time, right? How does that work?
rest results in a call to
seq on itself, which finally calls
.sval. Inside the
.sval call (and this is now runtime) the thunk bound to the deftype member
fn is called resulting in a new call to the
lazy-seq macro. It seems there is some interleaving of macroexpansion and runtime, or I'm misunderstanding the implementation.
Take my comment with a big grain of salt, since I am much less familiar with ClojureScript than Clojure/Java, but at least in Clojure/Java when you do a
defn containing one or more uses of macros, those macros are expanded while compiling the entire
defn form, not at run time.
i.e. at run time, there should be no remaining occurrences of
lazy-seq or any other macro call.
What's confusing me is that at compiletime, (lazy-seq some-body) gets macroexpanded into the LazySeq deftype
Part of your original statement was "resulting in a new call to the
lazy-seq macro". I do not think that is true.
(new cljs.core/LazySeq nil (fn  [email protected]) nil nil))`
(new cljs.core/LazySeq nil (fn  [email protected]) nil nil))
and when you call any ISeq function on that, there is another call to
lazy-seq that occurs, which appears to be at runtime
Every occurrence of a
lazy-seq macro should be replaced by an expression like its body at compile time, yes, and what you pasted above is what I see in ClojureScript source code as its body.
That body contains no occurrences of the
lazy-seq macro, though, so how would
lazy-seq be involved at run time?
A procedure which, at compile time, had all of its occurrences of
lazy-seq replaced with the body of
You could write
my-iterate without using
lazy-seq at all, just using the body of
lazy-seq yourself, and you should get the same effect as if you used
Unless I have messed something up in my copy and paste (possible), you could write
my-iterate like this:
(defn my-iterate [f x] (new cljs.core/LazySeq nil (fn  (cons x (my-iterate f (f x)))) nil nil))
and that is what the compiler sees and actually compiles, after it has done macro invocation
right. Nothing named
lazy-seq remains after macro expansion
or slightly more precisely, the function that expression evaluates to.
it calls that already-compiled body.
there is no
lazy-seq remaining anywhere at this point.
there is the function that looks like what I pasted above.
It creates and returns a new LazySeq object.
(after first evaluating the
(f x) part of the expression)
Think of them as a code-writing convenience, and they are all gone by the time "the real compiler" sees the ClojureScript code.
not exactly accurate, since macro expansion is part of the compiler, but perhaps useful in thinking of the distinction of macro expansion time.
I understand the concept of macroexpansion time and the fact that all macro calls are replaced with their expansions by the time runtime comes around
this is odd because at runtime there is a call that uses some already expanded code.. ors omething
the way I have been thinking about it is that any macros the compiler can see, in source text, get replaced... and then you're done and on to runtime
I completely understand if that plus recursion can throw one for a mental loop, though.
The post-macroexpanded code is a function that returns a new object. One field of that object happens to be a reference to an already-compiled function, in this case an anonymous one that happens to call the outer function.
Cut yourself a little slack -- I think it has that effect on lots of people the first time they understand it (and many just use it without trying to pick it apart).
there is always the draw over time to try and pick apart clojure/script source for me... hah
It reminds me of the saying "In order to understand recursion, one must first understand recursion"