Fork me on GitHub
Lucy Wang02:09:31

Both CLJ & CLJS tests of GH-141 branch passes on my dev machine.

👍 3

Thanks Lucy. I’m going to add one more test and then I will merge the branch once it passes CI.


Tim, I haven’t had a chance to run this against the case you have an open ticket for. I’m hoping I’ll have a chance tonight.


☝️ Fixes the SO from GH-141


I don’t think this fixes the other SO ticket but I haven’t checked yet.


Looks like, no, it doesn’t.


I’ll start investigating it tomorrow.


Just pushed the tail recursive non-recur and now the other one doesn't stack overflow with a stack of 512k.

clj -J-Xss512k -Sdeps '{:deps {meander/epsilon {:local/root "."}}}' -e "(require '[meander.epsilon :as m])  (defn z [] (m/rewrite {} (loop* [?seq (clojure.core/seq ?xs) ?chunk nil ?chunkn 0 ?i 0] (if (clojure.lang.Numbers/lt ?i ?chunkn) (let* [(m/and ?sym (m/app always-meta {:tag  ?tag :type ?type})) (.nth ?chunk ?i)] (do ?body (recur ?seq ?chunk ?chunkn (clojure.lang.Numbers/unchecked_inc ?i)))) (let* [?as (clojure.core/seq ?seq)] (if ?as (let* [?bs ?as] (if (clojure.core/chunked-seq? ?bs) (let* [?cs (clojure.core/chunk-first ?bs)] (recur (clojure.core/chunk-rest ?bs) ?cs (clojure.lang.RT/intCast (clojure.lang.RT/count ?cs)) (clojure.lang.RT/intCast 0))) (let* [?sym (clojure.core/first ?bs)] (do ?body (recur (clojure.core/next ?bs) nil 0 0))))))))) (foreach ~(or ?type ?tag) ?sym ?xs (m/app inner-form ?body))))"
If you have lastest meander locally you can run this from its root. I actually tested and with this change 511k is the smallest you can go and still compile it. Any lower it stack overflows.


I was hoping something like kondo would give a warning about this. But it doesn't. If anyone knows a tool that can lint non-recur tail recursion, that would be great to find.


We could probably trampoline walk/prewalk/postwalk and get our stack size much smaller. Obviously with a macroexpansion performance hit.


As I'm looking at things, walk seems to be the thing that grows our stack most often. So if we want to reduce it, making some walk that didn't grow the stack would definitely be the approach we should take. Just fyi, default stack of 32bit is 320k default for 64bit is 1024k.


Thanks for looking at this!


We could trade stack for heap and use a zipper approach but that implies filling in blanks for the usual zipper stuff.


I think ref substitution is where we end up eating the most stack.