Skip to content

Instantly share code, notes, and snippets.

@trptcolin
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trptcolin/8f91cdb2eddb6e7d9adc to your computer and use it in GitHub Desktop.
Save trptcolin/8f91cdb2eddb6e7d9adc to your computer and use it in GitHub Desktop.

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)
@trptcolin
Copy link
Author

OK, I understand the above error now: the mapcat in choice assumes that the result of the continuation call will be seqable. So we need to wrap any results in a seq.

Also, it seems like return isn't buying us much as a macro, and we could actually do:

(def return list)

This would allow your desired version to work:

(reset
  (return
    (let [a (choice 1 2)
          b (choice :x :y)]
      [a b])))
;=> ([1 :x] [1 :y] [2 :x] [2 :y])

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment