Fork me on GitHub

so I've moved along nicely in my macro'ing so far


but am tripping up on my macro needing to write a defn


specifically that I want to bind something in the macro, and then have that outputted as the arguments to the defn


but because of the way bindings work after compile time, I don't think that's a thing I can do right? because I can't like


have bindings permeate up from macro-time into regular-time


(defmacro foo [a b c]
  (let [d "test"]
    `(defn [d] (str d "bar"))))


contrived example


so you want to create d inside the macro, and for that d to be used by the macro? are you intending that "test" become the name of the arg to the defn or? also that defn is missing a name, I don't know if that is on purpose


I'm not really sure what d should be doing here


not intentional, typo


hmm how better to explain this


basically the macro is to get rid of a bunch of boilerplate. every function that I am exposing in this SDK I'm writing has basically the same first 20 lines or so


and it's all just validation against the arguments + spec


so anyway, ignoring some gross code / probably me not doing this the most efficient way I can be


(defmacro def-sdk-fn [name spec topic body]
  `(defn ~name
     ([& args#]
      (let [args# (map iu/extract-params args#)
            callback# (second args#)]
        (if-let [error# (cond

                          (or (> (count args#) 2))
                          (do (js/console.log "args > 2")

                          (and (first args#)
                               (not (map? (first args#))))
                          (do (js/console.log "first arg not map")

                          (and (not (nil? callback#))
                               (not (fn? callback#)))
                          (do (js/console.log "cb isnt a fn")

                          :else false)]
          (p/publish {:topics ~topic
                      :error error#})
          (let [params# (first args#)
                params# (assoc params# :callback (second args#))]
            (if (not (s/valid? ~spec params#))
              (do (js/ "Params object failed spec validation: " (s/explain-data ~spec params#))
                  (p/publish {:topics ~topic
                              :error (e/invalid-args-error)}))


there's the macro itself


and how I'd like to use it is basically this:

(def-sdk-fn login
    (do (js/console.log "test" topic)
          (p/publish {:topics topic
                             :response "aww yiss"})))


note the "topic" usage in my using the macro


that's something that I'm both: - passing to the macro itself (3rd argument after name and spec) - wanting to be available as a binding to the ~body that they pass in


OK - this is called an anaphoric macro


they are well documented


clojure tends to discourage them, but they are possible by using a ' to prevent evaluation inside a ~ unquote


so you end up with ~'topic inside the code


also, the idomatic way to do a body for a macro is [... & body] in the args and then [email protected] in the code, then you don't need the do block in the input


for usage of topic in the body of the call to work, you might need something like [~'topic ~topic] in the let block


and my usage of the macro doesn't change in that case?


I just have a magical "topic" binding accessible to me


without visibly seeing where it comes from?


(if so, I can see why these are discouraged)


right, like I said these are called anaphoric macros, if you google for "anaphoric macro clojure" you'll probably see a few good examples of using the ~'foo combo


well it works


but i have that "dont do this gross thing" spidey feeling


thank you! 🙂

Drew Verlee14:04:49

Would it be resonable to solve problems the “Bridge Design Pattern” using multimethods? It seems to me the goal is to have flexibility in extending our types and the functions that operate over those types.

Drew Verlee15:04:24

Can a defrecord have two protocals it references?


@drewverlee Do you have an example? The text book shape-drawing-itself one doesn't seem like idiomatic clojure.

Drew Verlee15:04:57

@yonatanel The goal here is educational, I both want to understand a bit about these patterns and see how they relate between java and clojure. The second goals is that several of my co-workers have experessed their inerested in FP, but aren’t sure how to apply their oop patterns to FP. I assume many of the patterns to be some form of missing language feature.

Drew Verlee15:04:45

Best java example (i think at least) was from this:


Frankly in this kind of discussion you'll have to show them how to solve real problems, because in clojure you'll just have {:shape :circle, :color :red} and any number of functions that can draw them with different implementations.

Drew Verlee15:04:18

I agree. It helped a lot of have someone else echo my thoughts. An actual problem would probably be better, but also more time consuming and harder to understand, ill see what i can come up with 🙂


@drewverlee I'm interested if you're trying to convince everyone to switch to clojure at the workplace or if you're just talking about it, and how it goes :)


@drewverlee here is a link to one way to tackle the problem Bridge pattern is trying to solve using Clojure This example uses multi methods as you suggest but also introduces hierarchy in the keywords to dispatch on (i.e. keywords can derive from other keywords). Lots of other interesting patterns in the link BTW

Drew Verlee16:04:43

Thanks agile geek, i have worked though some of those. I really like it, but I wanted to create something more technically in depth


@drewverlee I think that Bridge pattern example shows the fundamental difference between OOP and FP in it's approach. In the FP version only the data has a hierarchy. I.e. data can inherit but behaviour cannot. You can dispatch to different implementations of behaviour based on data. It's the clean separation of data and behaviour that makes it powerful. I don't know how often in OOP I've made the point that you should not inherit only for behaviour but delegate. It's great to have a language that reinforces that concept.

Drew Verlee16:04:03

Very well put. That's a great way of comparing the two styles.


Hi. Is there any way to "alias" a classname? trying to do something like this:

(def assertion-error
  #?(:clj java.lang.AssertionError :cljs js/Error))
(is (thrown? assertion-error (assert nil)))
Works in cljs, but clj complains about being "Unable to resolve classname: assertion-error"


since is is a macro, you might need to write a macro for this


(or rewrite is to evaluate the first arg to a thrown? clause)


iirc is uses a multimethod, and one of the method dispatches hard-codes the path for thrown?


i see, thanks


yes, you are correct regarding use of a multimethod (which is exactly the same in clj and cljs, btw)


so you could just use defmethod to override that multi... monkey-patch style - this may be a bad idea though


i guess I could write a assertion-thrown?


oh yeah - and extend it yourself - that's much more sensible


I recently had a related problem (which is why I know about is using a multimethod etc. etc.) it turns out that futures capture Exceptions and you can try/catch around the deref, but AssertionErrors don't behave as nicely / testably. I basically just gave up on using AssertionErrors so that the code would be testable.


no futures here -- this is cljs 😛


well, no AssertionErrors in cljs either...