Fork me on GitHub
#beginners
<
2022-03-06
>
Daniel Shriki09:03:21

Hi everyone 🙂 using Polylith - how do I include a jar file in my project? (jar that has compiled with java)

Neil Barrett17:03:01

Hi - I am trying to look at some real code to improve my Clojure knowledge. The real world is way tougher than the books! Anyway, I chose cljfx as a first project. I have a question relating to protocols. The project defines the following protocol in lifecycle.clj:

(defprotocol Lifecycle
  :extend-via-metadata true
  (create [this desc opts] "Creates component")
  (advance [this component desc opts] "Advances component")
  (delete [this component opts] "Deletes component"))
This protocol is implemented in the same file by
(def root
  (with-meta
    [::root]
    {`create (fn [_ desc opts]
               (binding [*in-progress?* true]
                 (create dynamic desc opts)))
     `advance (fn [_ component desc opts]
                (binding [*in-progress?* true]
                  (advance dynamic component desc opts)))
     `delete (fn [_ component opts]
               (binding [*in-progress?* true]
                 (delete dynamic component opts)))}))
In https://clojure.org/reference/protocols, I see that protocols can be implemented by values through attaching metadata implementations of the required functions. 1. Is the value here the vector [::root]? 2. I don't understand the relationship between the root in the def and ::root. 3. In these metadata implementations of protocols, does Clojure look for the protocol being implemented in the current namespace? Don't we need to specify the protocol's name somewhere in the metadata? 4. Can more than one protocol be implemented in this way? Would that involve just adding another map?

hiredman18:03:10

Yes, there is none, no it's in the metadata, yes

hiredman18:03:44

And no (#4 is a two parter)

Neil Barrett18:03:34

Would (4) involve adding all the functions to the same map?

oliver marks19:03:25

I want to create a function that wraps some code, I know i can wrap the block in a function and pass that in as a parameter but curious if thats the only / best way, I am after something like a with where I can initialize run a block of code then run an ending function in clojurescript. think timeit or file close type function

walterl21:03:04

So basically a decorator, right? There's https://clojuredocs.org/clojure.core/alter-var-root (a):

(def my-add [a b]
  (+ a b))

(var-alter-root #'my-add decorator)
But if it feels yucky, that's because it is. my-add no longer does what the code says it does. Rather be explicit about it (b):
(defn my-add' [a b] ...)

(def my-add (decorator my-add))
Or, if your circumstances allow, don't define a statically decorated var at all, and just pass (decorator my-add) where-ever it's necessary. (c) Then, you're right, a macro could do the job here, in the form of something like a defdecorated macro that expands to a defn + one of the options above, or a (def ~fn-name (decorated (fn [& args] [email protected]))). I've done the latter before, for functions to be executed on other, worker nodes, but regretted doing so, because it strongly ties your decorator to your decorated function. What worked better was adding the decorator at the call site. IMO if the decorator should always be part of the function call, go with (b), otherwise (c).

oliver marks06:03:55

(defn timeit [name & code]
(let [result (apply callback)])
(initialize)
(finalize)
result)

(timeit
 (prn 1)
 (prn 2)
 (prn 3))
This is a bit like a decorator, though I would like to be able to use it not just with a function, so see the above code where I want to send in 3 print statements and have them run capture and return the result. A macro will expand the body in place so would work just curious if there are other option I have missed as you see this in other core functions but i notice a lot are macro's so things like let do & when are examples where you would wrap a block of code.

walterl20:03:39

It's not super clear to me; what would the macro-expanded code look like? Side note: implied in my previous response is the hint to not use a macro unless it's really necessary. I.e. in most cases it's not the right answer.

oliver marks19:03:31

a macro or promise could be used, but not sure about the wrapping the statements in another function and what options there are on this front