Welcome! Please see the About page for a little more info on how this works.

0 votes
in core.async by
(case-let) is a macro to handle messages of the form {{[:message-tag, arg1, arg2, ...]}} with the arguments bound to local variables. It fails to work correctly when used within a go block. Note that a simple macro with a case, e.g. (defmacro my-case [expr & cases] `(case ~expr ~@cases)) does work.

(Sample project attached)

(case-let) definition:


(ns core-async-bug.macros)

(defmacro case-let [expr & cases]
  (let [msg (gensym)]
    `(let [~msg ~expr]
       (case (first ~msg)
         ~@(apply concat (for [[key [args & body]] (partition 2 cases)]
                    [key `(let [~args (rest ~msg)] ~@body)]))))))


ClojureScript test code:


(ns core-async-bug.core
  (:require-macros [cljs.core.async.macros :refer [go]]
                   [core-async-bug.macros :refer [case-let]])
  (:require [cljs.core.async :refer[<! put! chan]]))

(enable-console-print!)

; go block with manual case + lets - works
(let [c (chan)]
  (go
    (let [msg (<! c)]
      (case (first msg)
        :a (let [[x] (rest msg)] (println "First :a" x))
        :b (let [[y] (rest msg)] (println "First :b" y)))))
  (put! c [:b 123]))

; case-let outside of go - works
(case-let [:b 123]
  :a ([x] (println "Second :a" x))
  :b ([y] (println "Second :b" y)))

; case-let inside go - broken
(let [c (chan)]
  (go
    (case-let (<! c)
      :a ([x] (println "Third :a" x))
      :b ([y] (println "Third :b" y))))
  (put! c [:b 123]))


Browser console output:


Second :b 123
First :b 123
Third :a 123          <-- Should not be there!
Third :b 123

3 Answers

0 votes
by

Comment made by: tslocke

More discussion here: https://groups.google.com/forum/#!topic/clojurescript/w21nNWkKR-c

0 votes
by
_Comment made by: tslocke_

I've discovered an easy workaround for this problem. During macro-expansion core names like {{case}} become fully qualified, i.e. {{cljs.core/case}}, and it seems that the go macro then fails to recognise the case as such. Replacing {{case}} with {{~'case}} in the definition of {{let-case}} fixes the problem.

I would hope this leads to an easy fix for someone who knows the core.async codebase.

The working macro is:


(defmacro case-let [expr & cases]
  (let [msg (gensym)]
    `(let [~msg ~expr]
       (~'case (first ~msg)
         ~@(apply concat (for [[key [args & body]] (partition 2 cases)]
                    [key `(let [~args (rest ~msg)] ~@body)]))))))
0 votes
by
Reference: https://clojure.atlassian.net/browse/ASYNC-79 (reported by alex+import)
...