code-reviews

2020-09-05T04:18:50.047400Z

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

2020-09-06T03:58:09.048500Z

Interesting, thanks Sean! Will play with this more

2020-09-06T21:08:25.049Z

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?

seancorfield 2020-09-06T22:06:04.049200Z

@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.

1❤️
seancorfield 2020-09-06T22:06:42.049400Z

Without seeing a self-contained, repeatable project up on GitHub containing this code, it would be hard for me to suggest anything further.

2020-09-07T17:23:16.049800Z

Thanks Sean! https://github.com/stopachka/interleave-demo-for-sean ---

2020-09-07T17:23:46.050100Z

Okay I think I understand why my thing is not working. Indeed it is trying to eval in a loop

2020-09-07T17:25:09.050300Z

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.

seancorfield 2020-09-07T17:31:55.050500Z

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.

1👍
seancorfield 2020-09-07T17:33:01.050700Z

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.

seancorfield 2020-09-07T17:36:19.050900Z

(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.

seancorfield 2020-09-07T17:36:48.051100Z

Does that help @stopachka?

2020-09-07T17:41:49.051500Z

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?

seancorfield 2020-09-07T17:43:46.051700Z

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=> 

seancorfield 2020-09-07T17:44:11.051900Z

It's processing the form [1 2 3] at compile-time, not the runtime value [1 2 3].

seancorfield 2020-09-07T17:44:31.052100Z

And the form shrinks on each expansion so it bottoms out.

seancorfield 2020-09-07T17:46:06.052300Z

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.

seancorfield 2020-09-07T17:46:19.052500Z

Macros work on code, not values.

2020-09-07T18:12:14.052900Z

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!

1
seancorfield 2020-09-05T04:40:43.047600Z

@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

seancorfield 2020-09-05T04:41:17.047800Z

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=>