Ah, fixed this by writing interleave like this
(defn interleave* [s1 delayed-s2]
(cons-stream (car-stream s1) (interleave* (my-force delayed-s2) (my-delay (cdr-stream s1)))))
(defmacro interleave [s1 s2]
`(interleave* ~s1 (my-delay ~s2)))
I think the error above came from a fundamental misunderstanding of macros, and was worsened by the fact that interleave already exists:
(defmacro interleave [s1 s2]
`(cons-stream (car-stream ~s1) (interleave ~s2 (cdr-stream ~s1))))
I defined this as a recursive function, but i realize this can’t work for macros — it would need to expand all the time. I guess it evaled because interleave
inside the macro referred to cojure.core/interleave
Interesting, thanks Sean! Will play with this more
Hey Sean, tried playing with this a bit:
Based on trial
, it seems like interleave
should work as a recursive macro too (as it only works on the forms)
(defmacro rec-interleave [s1 s2]
`(cons-stream (car-stream ~s1)
(rec-interleave ~s2 (cdr-stream ~s1))))
macroexpand seems to do what I expect:
(macroexpand '(rec-interleave integers integers))
=> [(chp3/car-stream integers) (chp3/my-delay (chp3/rec-interleave integers (chp3/cdr-stream integers)))]
Buut, if I try to evaluate
(def x (rec-interleave integers integers))
Execution error (NoClassDefFoundError) at nrepl.middleware.interruptible-eval/evaluate (interruptible_eval.clj:79).
Could not initialize class clojure.lang.Compiler$CompilerException
---
Am not quite sure how to debug further from that error. Do you have thoughts on what could be happening?@stopachka My first suspicion would be that it is an infinite evaluation. But I always advise debugging things in a plain REPL, without nREPL and all that other stuff that tries to "help" manage evaluation and display of results.
Without seeing a self-contained, repeatable project up on GitHub containing this code, it would be hard for me to suggest anything further.
Thanks Sean! https://github.com/stopachka/interleave-demo-for-sean ---
Okay I think I understand why my thing is not working. Indeed it is trying to eval in a loop
I am not sure how my interleave is different from the trial
one:
Perhaps the big thing is the if
— if doesn’t eval the the forms until runtime, while mine evals at compile time.
Just looked at this -- now I can see what the other functions/macros are -- and you're falling into the same trap I talked about above: your macroexpansion has no "bottom" at compile time so it just recursively expands until you get a stackoverflow.
The example I gave works because it is analyzing forms at compile time and so it can tell when it hits the end of a form. It wouldn't work with bound names because those are not sequence forms.
(rec-interleave integers integers)
expands to [(interleave.demo/car-stream integers) (interleave.demo/my-delay (interleave.demo/rec-interleave integers (interleave.demo/cdr-stream integers)))]
but in trying to evaluate that the remaining rec-interleave
call is macroexpanded again and it always produces a larger form. Hence the infinite loop during evaluation.
Does that help @stopachka?
Am close but don’t quite get the difference between trial and interleave:
> It wouldn’t work with bound names because those are not sequence forms.
Do you mind expounding a bit? What is the difference between a bound
name and a sequence
form?
i.e:
(defmacro trial [x]
(if (seq x)
`(do
(println ~(first x))
(trial ~(rest x)))
:done))
How does the compiler know that it shouldn’t do anything when it sees trial
in the do
expression?It does do something -- it expands it. But pay attention to what actually happens as you expand it each time:
user=> (macroexpand '(trial [1 2 3]))
(do (clojure.core/println 1) (user/trial (2 3)))
user=> (macroexpand '(trial [2 3]))
(do (clojure.core/println 2) (user/trial (3)))
user=> (macroexpand '(trial [3]))
(do (clojure.core/println 3) (user/trial ()))
user=> (macroexpand '(trial []))
:done
user=>
It's processing the form [1 2 3]
at compile-time, not the runtime value [1 2 3]
.
And the form shrinks on each expansion so it bottoms out.
But in
user=> (def x [1 2 3])
#'user/x
user=> (macroexpand '(trial x))
Syntax error macroexpanding trial at (REPL:12:1).
Don't know how to create ISeq from: clojure.lang.Symbol
user=>
the x
is not a sequence so the compile-time call to seq
fails -- x
is not evaluated here because we're in the Read phase still, we haven't reached evaluation, so there are no runtime values.Macros work on code, not values.
Aah I see! So if we had passed in (trial [foo bar]) It would do something like print ’foo ’bar --- Oky doke this makes a lot of sense. Thanks Sean!
@stopachka you can have recursive macros, but they have to operate on the form passed in, not the value:
user=> (defmacro trial [x]
(if (seq x)
`(do (println ~(first x)) (trial ~(rest x)))
:done))
#'user/trial
user=> (trial [1 2 3])
1
2
3
:done
user=>
This breaks down the sequence passed in directly and either returns a form that recursively expands only the tail of the sequence, or a non-recursive expression :done
user=> (macroexpand '(trial [1 2 3]))
(do (clojure.core/println 1) (user/trial (2 3)))
user=> (macroexpand '(trial (2 3)))
(do (clojure.core/println 2) (user/trial (3)))
user=> (macroexpand '(trial (3)))
(do (clojure.core/println 3) (user/trial ()))
user=> (macroexpand '(trial ()))
:done
user=>