Fork me on GitHub
#clojure
<
2020-08-27
>
athomasoriginal00:08:35

@deactivateduser10790 thank you for sharing your process! @smith.adriane No worries, Aero is just a detail we can ignore that for now. The general question is: is there a downside to adding the root of ones project to the classpath? I have everything working, but i’m just curious at this point :thinking_face: The driver of the question: io/resource would need the root of the project that in order to find a file at the root.

deactivateduser00:08:42

@tkjone my only thought would be whether having two roots that overlap (`.` and ./src) might cause issues. I don’t think I’ve ever dug into what the Java classloader(s) do in that case, let alone the Clojure classloader(s).

deactivateduser00:08:23

It’s certainly not done much in Java (or, I suspect, Clojure), so I would be a bit hesitant to do it, albeit without any specific objections to cite…

athomasoriginal00:08:03

Good point and yeah it’s not something I’ve see either.

cpmcdaniel14:08:09

I have a pattern of many functions like this where the first two args are always the same spec, but the third argument varies (always a seq of at least two items, the first always being a keyword). Thus, it's the very last item in my spec ::proj where the variation will happen. Can s/cat be composed in a way that I can DRY this up?

Alex Miller (Clojure team)14:08:22

no? I don't see any R to D

cpmcdaniel16:08:41

Another spec question: I was hoping (s/spec? ::my-spec) would work, but I see there is also get-spec . Is there anything that combines the two (returns true if the argument is either a spec or keyword for a spec in the registry)?

cpmcdaniel16:08:35

most other calls in the library can take a spec-or-kw param. I just thought spec? would do the same.

dominicm19:08:37

I want to implement a CRDT in Clojure, specifically a LWW Element-Set (which is more like a hashmap). I'd like to have core functions like merge work on it, but conj would need to take a time that the k/v is true as-of. Does anyone have some advice?

lilactown19:08:16

I’ve thought about this a bit. I think you could handle this using a dynamic var & binding

lilactown19:08:45

something like:

(crdt/as-of (now)
  (merge my-crdt {:foo "bar"}))

dpsutton19:08:59

if they need to be run in some kind of context i'd probably eschew the standard abstractions

dpsutton19:08:29

(having thought about it for all of 10 seconds but that's where i am)

dominicm19:08:20

@lilactown that was my goto as well, although dynamic vars are always suspect for this kind of thing. They're noticeably missing from any core data structures for example.

dominicm19:08:55

I suppose you could get a new data structure by doing (merge (updating-to my-crdt (now)) {:foo "bar"}). There's a risk of someone accidentally reusing an old time they didn't intend I suppose.

lilactown19:08:18

that might be a decent way of doing it, yeah

lilactown19:08:37

dynamic variables have tradeoffs. there are performance & complexity implications that can detract from it. the benefit is that you can easily reuse core fns, and also provide runtime verification. e.g. you can throw if it’s not inside an as-of

lilactown19:08:48

you can also throw if you nest as-of

dominicm20:08:11

One problem with building on core is that you might violate expectations:

(defn foo [doc a]
  (let [x (merge doc a)]
    (is (true? (= (get x :a) (get a :a))))))
Can't think of much which would do that, but still.

lilactown20:08:15

i’m not sure what would be violated in this case?

lilactown20:08:16

seems like you would want to maintain that semantic of merge

hiredman20:08:40

clojure.core/merge never removes an entry

hiredman20:08:56

merging crdts can

hiredman21:08:37

clojure.core/merge is about merging values, merging crdts is about merging the casual history of the crdts and constructing a new value that is the result of the merged history

lilactown21:08:10

yeah if the semantics are different I wouldn’t reuse clojure.core then

👆 3
dominicm21:08:43

It's very convenient to have access to the wide array of key renames and such that we have access to via clojure core though.

dominicm21:08:09

There's only a handful of functions which modify a map and then read it back with assumptions it can read it's changes.

hiredman21:08:39

there are operations on the point in time value of the crdt, and there are operations on the casual history, I think you can potentially use the clojure.core functions for operations on the current value, with the implicit time of now, and then another set of functions for operating on the history

hiredman21:08:13

so clojure.core/merge of crdt1 and crdt2 would act like merging maps, ignoring the history that crdt2 might have a history of some keys from crdt1 being deleted

hiredman21:08:45

and you would have a crdt/merge (I've used join as a name for this in the past) that would merge crdts respecting history

lilactown21:08:56

it seems like what you really want is to be able to easily go to and from a hash map and a CRDT

lilactown21:08:00

rather than operating on the CRDT itself

lilactown21:08:13

haven’t thought enough about CRDTs specifically

lilactown21:08:09

(-> crdt
    (as-of (now)) ;; returns a map
    (merge {:foo-bar}) ;; returns a map
    (->> (crdt/update crdt (now))) ;; returns the new crdt with the new value updated as of time `(now)
` you could also do what hiredman said and create a new crdt2 out of the newly created map and join them

dominicm21:08:58

I'm possibly looking at something closer to an ORDT it seems. I can prune history pretty aggressively. I'm not looking much at the causal history. All operations are about adding information and only having it update if it's newer than what is already there.

dominicm21:08:19

last write wins, where the time varies per-key for both source and merge.

hiredman21:08:56

the causal history is what tells you what is newer

hiredman22:08:34

I don't think I've seen crdt datastructures that aren't last write wins, the way they are categorized is how concurrent operations are resolved (add wins, remove wins etc)

Chris O’Donnell22:08:10

At least some CRDTs designed for collaborative text editing can have more elaborate conflict resolution strategies. Martin Kleppmann has an interesting talk on the subject at https://martin.kleppmann.com/2020/07/06/crdt-hard-parts-hydra.html. Of course that's a bit of a different use case than a generic hash map.