I’m digging into delimc, which is pretty awesome stuff, and I ran into a question when writing a shift/reset version of amb:
(use ‘delimc.core)
(defmacro amb [xs]
`(shift k# (dorun (map k# ~xs))))
(reset
(let [a (amb [1 2])
b (amb [:x :y])]
(prn (list a b))))
;; (1 :x)
;; (1 :y)
;; (2 :x)
;; (2 :y)
;=> nil
OK, it’s generating the right stuff and is generally awesome. What I’d really like is to be able to collect each result without side effects.
(defmacro amb [xs]
`(shift k# (map k# ~xs)))
(reset
(let [a (amb [1 2])
b (amb [:x :y])]
(list a b)))
;=> (((1 :x) (1 :y)) ((2 :x) (2 :y)))
This gives me more nesting than I’d like - I was hoping to get:
((1 :x) (1 :y) (2 :x) (2 :y))
If I print out the value of a
and b
at any given point, I only ever have a single value (1
, 2
, :x
, or :y
), so it’s the collection of results, not their emission, that’s at issue here.
For this particular example I can always (mapcat identity …) on the result, but I’d have to do that multiple times if I add more ambs/shifts into the mix.
I definitely don’t fully grasp the CPS transformations that are going on under the hood, but I’m wondering if anyone knows of:
- (a) some way to emit results without side effects (clearly I could just
swap!
/conj
each result into an atom like I did w/ printing), - (b) something inherent to the shift/reset model that makes what I want impossible or not a good idea, or
- (c) a way to change the underlying transformations to get what I want (patch to delimc or other)