This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # announcements (1)
- # architecture (8)
- # babashka (8)
- # beginners (68)
- # biff (1)
- # calva (2)
- # clj-kondo (13)
- # cljs-dev (2)
- # clojure (71)
- # clojure-art (26)
- # clojure-europe (14)
- # clojure-nl (10)
- # clojure-uk (4)
- # clojurescript (96)
- # community-development (6)
- # conjure (1)
- # datalog (2)
- # emacs (6)
- # fulcro (20)
- # hugsql (7)
- # lsp (6)
- # nextjournal (13)
- # off-topic (7)
- # portal (1)
- # reagent (3)
- # reveal (8)
- # sci (50)
- # shadow-cljs (8)
- # spacemacs (2)
- # tools-deps (9)
- # xtdb (6)
what are all the main component libraries in use in recent days , sierra’s, mount, integrant/duct, am missing any ?
@jasonjckn Also https://github.com/juxt/clip and I think there's been another one pop up recently...
@seancorfield do you have a preferred one yourself
We are big fans of Component at work.
Can someone explain at a high level why stuarts Component uses clojure meta data? This surprise me because i always thought meta was mostly useful for tooling. E.g editors. Because meta is data that isn't used in equality and i assume is complied out of prod builds. The later is obvious not true sense it's being used here... https://github.com/stuartsierra/component/blob/master/src/com/stuartsierra/component.cljc
Metadata is a first-class feature that many clojure.core functions will retain It would take a very explicit step to exclude metadata, e.g. setting https://clojure.org/reference/compilation#_elide_meta and including specific keys there
Metadata is used a lot more extensively than you might think.
Being able to extend protocols by adding metadata to objects is a game changer, as far as I'm concerned, so I think you'll see the use of metadata increase now that's possible. @drewverlee
@UGC0NEP4Y @U01G423GNFP Clojure 1.10 introduced the ability to satisfy protocols by adding metadata to (Clojure) objects: https://github.com/clojure/clojure/blob/master/changes.md#22-protocol-extension-by-metadata -- this means you can provide your own implementation to any such open protocol, attached to any Clojure object (that implements
clojure.lang.IObj), and pass that object into code that relies on those protocols -- without statically extending the protocol to a type.
For example, with Component, you can use plain hash maps or even functions as "components" because you can attach
start/`stop` lifecycle functions to them.
next.jdbc does this so that it can offer (optional) support for Component, without needing to specify Component as a dependency (which would force it on other users): https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/connection.clj#L320-L328
You can see there it uses a plain, empty hash map for the component that you can call
start on, and returns a function that you can call
next.jdbc itself has a number of protocols, several of which can be extended dynamically via metadata this way, including https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/prepare.clj#L29 (`SettableParameter`) so that users do not need to extend that protocol to the various types of value they pass as parameters: if they need special parameter treatment, they can just attach it as metadata, in the call as needed. And
next.jdbc.types provides metadata-based wrappers for all SQL types that you can use to wrap any object (`IObj`) passed as a parameter to have it set as a set as a parameter using that JDBC type instead of just
In addition, I believe several Clojure databases are using metadata to attach additional information to entities -- while not breaking equality semantics -- to provide added data about the entity. That's a bit hand-wavy because I'm not familiar enough with them to point you to specifics (and I haven't confirmed it -- just going on comments I've seen going back and forth in some channels).
Another good example is
datafy/`nav` (also new in Clojure 1.10) but I think you can argue that falls under "tooling" rather than "production" use. Still, it's yet another example of being able to satisfy arbitrary protocols, at runtime, without needing to extend them to all values of any given type.
thanks sean, thats really interesting.
@seancorfield Thanks for the detailed explanation. That said, it seems protocol is flexible and power enough. Under what circumstance shall we use multimethod? Any good use case?
Protocol dispatch by type is fast (type of the first argument). Protocol dispatch via metadata is slower, but very flexible, and again it's off the first argument. Multimethods dispatch off arbitrary computed values and can work across all arguments. Both systems are open-to-extension (which is the Clojure way).
At work, we have a multimethod for handling email status updates, for example, because the action to take is based off two things: the new status update and the current status information -- can't be a protocol.
I feel multimethod is ONLY appropriate for some utility functions, for example, (defmulti print-me) which dispatches on the printed object, and the example you make also sounds like a utility function. Multimethod is NOT suitable for cases where: We have a certain object and we have several functions to dispatch on this object. It is of course doable, but not applicable because mutlimethod is so loose, it does not convey the concept that some functions belong together serving for a single purpose whereas Protocol can.
Multimethods have their place. We have 15 in a codebase of 130K lines. Some people are going to use them more. Those 15
defmulti have 164
defmethod between them. They all tend to be dispatch-on-value rather than dispatch-on-type.
@drewverlee extend-via-metadata is a relatively recent feature in Clojure's history. you can extend any object that can carry metadata to implement a protocol now and component just added support for this feature: you don't have to use it, but you can.
How do you relate
reify with extend-via-metadata. I don’t quite understand how convenient extend-via is
extend-via-metada doesn't require you to make a bridge between the reified interfaces/methods that the object implements, the object itself is still just the object, you're just adding an ad-hoc implementation to it
I didn’t see concrete examples that
extend-via-metada brings in. Other than it breaks
I think it was implemented primarily for the Navigable protocol. Personally, I use it to make database query results Navigable (works in Reveal, REBL, etc.). Having to implement a custom type for that would suck.
Here it is in action: https://github.com/kuhumcst/DanNet/blob/master/src/main/dk/cst/dannet/query.clj#L49-L67
This is probably best example on how you can implement a Component lifecycle without a record and explicitly depending on Component : https://github.com/seancorfield/next-jdbc/blob/38b924b36ea29c416767e96dfc447cd62c1125d4/src/next/jdbc/connection.clj#L297-L328
I’m looking for a generic comparison function that would allow comparison across different types like
(compare 0 "") as you can with
< and friends in Datomic. Does anyone know of a library that does this? https://github.com/clojure/algo.generic seems not to be it…
Just a "maybe", haven't thought too much about it:
(defn compare+ [a b] (let [at (type a) bt (type b)] (if (= at bt) (compare a b) (compare (str at) (str bt)))))
Yeah that’s how I started! 🙂 But that will not compare numbers correctly:
I started reverse engineering what’s in Datomic and I’ve gotten pretty far. I just wonder if someone else has done the same before.
user=> (compare 0 0.0) 0 user=> (compare+ 0 0.0) 8
There is also a special case with vectors and lists.
Found something: https://clojure.org/guides/comparators#_comparators_that_work_between_different_types
Ah! Thank you!
Even that function might have some gaps in its implementation -- not sure.
But feel free to use it, extend it, change it, if it helps you with anything.
There's also https://github.com/greglook/clj-arrangement
@UDXEK491P thank you, it looks very similar to what I arrived at. Will try and share it.
Does anyone have any tips for writing unit tests w/ hato/jdk's HttpClient? I want to check what its called with and fabricate some responses. (starting a real http server to talk to is an option, just want to know if there are any options before that layer, and I am separately unit testing call sites w/ a classic protocol switch)
(seq? [1 2 3]) false?
Because vectors don't implement
ISeq. "A seq" and "a sequential collection" are not the same.
thanks. I’ll think I’ll have to review the relationships between coll , seq, sequential
a vector is seqable, that is "can produce a seq", but is not itself a seq
Trying to get going with
criterium for the first time, but as soon as I evaluate any benchmarking expression at the REPL, the Clojure process hogs up my CPU and hangs.
2019 macbook pro on catalina, criterium 0.4.6, clojure repl started via Calva jack-in
Have you tried leaving it running for a few minutes on something simple like
(time (reduce + (map (fn [x] (/ x 100.0)) (range 100)))) clj꞉user꞉> "Elapsed time: 0.216805 msecs" 49.5
> (c/quick-bench (inc 1)) Evaluation count : 55276344 in 6 samples of 9212724 calls. Execution time mean : 3.319750 ns Execution time std-deviation : 0.881184 ns Execution time lower quantile : 2.238889 ns ( 2.5%) Execution time upper quantile : 4.306481 ns (97.5%) Overhead used : 7.096611 ns nil
IIRC, the default time might be 60 sec, which only begins after an ~10-sec warmup time.
The defaults can be changed with the appropriate parameters.