Segfault? That sounds scary! I assume you mean a mistake.
When I run the function, I start getting errors of about 1000. So why does the stack overflow? This is due to laziness. The fix is to wrap the call for deletion in doall.
Reduce will go through each element in the sequence specified as the third argument, and save the state along this path. This state is initialized as the rage of integers from 2 to n + 1. The state is updated at each iteration using deletion. However, since remove is lazy, it will actually do nothing. Delete returns an object that can generate a sequence on demand, depending on the sequence it gave. I will try to explain this example:
(reduce (fn [xs x] (remove #{x} xs)) coll (range 4))
The above expression will return a sequence of coll elements, but with numbers from 0 to 3 it is filtered out. To explain what happens at runtime, I will come up with a new notation: I will write the runtime (remove #{x} xs) as «IOU a seq like xs but with x removed» . Then the meaning of the abbreviation expression is:
«IOU a seq like «IOU a seq like «IOU a seq like «IOU a seq like 'coll' but with 0 removed» but with 1 removed» but with 2 removed» but with 3 removed»
Each call to delete adds an "IOU" wrapper. This causes problems when you finally try to access the first value of the received seq (the outermost value is "IOU"). When the lazy-seq object passes, it is first checked to see if its value has been calculated. If the value has already been completed, the value is simply returned. If it is not, then it is calculated, stored, and returned.
The problem arises when one lazy-seq value ("IOU" value) forces another lazy-seq to do its job, because one stack frame is needed to implement lazy-seq. (In order to remember where to return when the second lazy seq is executed, a stack frame is needed.) If you have 1000 nested "IOUs", values, you need 1000 stack frames to implement them (provided that they were all unrealized at the beginning) .
The solution is to use the doall function from the Clojure standard library. It takes seq and makes it fully implement. If you end the call for deletion in doall, the reduction state will always contain a fully implemented seq between each iteration and without the "IOU" cascade, the values will increase. Instead of saving all the calculations for later use, you do them gradually.