Fork me on GitHub
#clojure
<
2022-08-25
>
Noah Bogart02:08:48

is there a multimethod-style functionality that allows for pairing predicate with fn? I'd love to provide a way for users to add branches/methods to my core set of preds/fns. i initially thought of multimethods, but that's a fixed predicate/function

hiredman02:08:06

You might look at custom hierarchies for multimethods, but those don't get used a ton

hiredman02:08:33

It is easy to forget that multimethods dispatch isn't actually strict equality, but actually uses isa? which is like equality plus if you have a multimethod defined for java.lang.CharSequence it will be used if the multimethod dispatch function returns java.lang.String

Noah Bogart03:08:25

I thought about that, but I'm hoping for "open ended" predicates. I'm hoping for a cond I can extend as I wish. The closest I've come up with is:

(def impls (atom []))
(defn register-impl [pred func]
  (swap! impls conj [pred func]))
(register-impl
  (fn [e _a] (fn? e))
  (fn [e a] (e a)))
(defn impl-chooser [e a]
  (reduce
    (fn [_ [pred func]]
      (when (pred e a)
        (reduced func)))
    nil
    @impls))
and then use it like:
(let [func (impl-chooser e a)]
  (if func
    (func e a)
    (= e a)))

walterl03:08:00

Not built-in, but https://github.com/camsaul/methodical :around methods may get you there.

👍 1
Mark Wardle11:08:03

Perhaps I'm misunderstanding what you are asking, but could you use a multimethod that uses a multimethod to dispatch upon?

Noah Bogart12:08:05

What’s the dispatch function of the inner multimethod? Lol “how deep did this go????”

jumar05:08:43

I never thought about using @ in combination with def. Just saw this in datomic docs: https://docs.datomic.com/cloud/tutorial/history.html#as-of-query

@(def recent-txns
   (d/q
    '[:find (max 3 ?tx)
      :where
      [?tx :db/txInstant]]
    (d/db conn)))

2
Daniel Tan12:08:04

what other ides allow selecting clojure forms via double click

delaguardo12:08:58

I use expand-region for emacs it explained very well https://emacsrocks.com/e09.html

tomd13:08:08

and you could bind to double-mouse-1 like so:

(define-key clojure-mode-map [double-mouse-1] 'er/expand-region)

👍 1
flowthing11:08:05

I would imagine most IDEs support expanding the selection to encompass the form under the cursor, although I’m not sure about using the mouse. Calva, for example: https://calva.io/paredit/#selecting Not to plug, but Tutkain (https://tutkain.flowthing.me) also does, and I would call it lightweight, it nothing else.

Daniel Tan12:08:01

i keep going back to cursive because of this one feature but I'm hoping for a more lightweight IDE

Bart Kleijngeld12:08:49

I could use some advice about dealing with schema evolution of Avro schemas.

Bart Kleijngeld12:08:22

After some reading about schema evolution in general, I've come to understand (and be convinced) that it's a good idea to keep all your fields optional to get full compatibility. However, when specifically reading about Avro, no one seems to advise this, but instead suggest providing default values with the record fields. Now, I see how this, indeed, does lead to full compatibility as well, but Chad Herrington (from Clojure's Lancaster library) has convinced me making the field truly optional (that is, type is union of null and the data type), is still a better idea. (https://github.com/deercreeklabs/lancaster/issues/21#issuecomment-1220055871)

Bart Kleijngeld12:08:00

So, my question is: 1. Do people here agree with Chad's (imo compelling) case that optional fields are preferable even over required fields with default values? 2. What reason could one have to favor required fields with default values?

lukasz14:08:26

Our use of Avro might be a bit exotic, but we've been always changing them in the following manner: • add new field, nullable • make old field, nullable • make new field required, remove old field Making everything optional at all times defeats the point of using Avro in the first place imho, but it depends on how you use them

Bart Kleijngeld16:08:20

Well the idea behind making everything optional is that you separate the concerns of evolution and validation. Evolution will be fully compatible at all times, and validation will be left to the consumer, which is a reasonable choice I'd say since it is hard to guarantee that (for example) all consumers will really require a field, now and in the future. Also, sometimes a specific consumer will want to add a field and state it as required, but other consumer might never use it. So I guess I'm curious in what sense you'd say it beats the point of Avro to make everything optional at all times. (Trying to learn here 🙂)

lukasz16:08:53

Ah yeah, these are valid points if you're using Avro with Kafka, our use of Avro is limited to RabbitMQ and own home-grown RPC system - so it's all about validation

Bart Kleijngeld16:08:15

Got it. I did approach this with Kafka in mind, wasn't aware to make that explicit. Thanks for sharing. It's nice to see another use case with its own choices

Daniils Petrovs14:08:31

Hey all, has someone used https://github.com/pmonks/spinner or similar before? Lost some hairs trying to understand why it takes > 20s for the program to actually complete. It finished the block inside of pi/animate! and then just waits for many seconds. E.g.

(pi/animate!
       (doto driver
         ...)
       (pi/print (green "Logged time for date: " date)))

p-himik14:08:37

My immediate guess is that it needs (shutdown-agents) at the end.

👀 1
Daniils Petrovs14:08:07

yep that was it, TIL thanks!

👍 2
Yogesvara Das16:08:57

Does something like this already exist anywhere?

hiredman16:08:45

there is a library called spectre that looks a lot like some of this

hiredman16:08:03

there are also these things from the functional programming world (haskell, idris, etc) called lenses and optics which also do similar kinds of things, and there are libraries for those in clojure as well

Yogesvara Das16:08:27

I'm using spectre already I don't think its api is remotely comparable to this specific usecase.

Yogesvara Das16:08:38

Tho it might help in implementing the function

seancorfield16:08:34

There's also Meander which I believe is similar? (never used either)

Yogesvara Das16:08:22

Meander's examples aren't making a lick of sense to me

Yogesvara Das16:08:44

this just looks like pattern matching?

emccue16:08:51

This is a kinda neat concept - lmk if you need help implementing it

hiredman16:08:53

I haven't used spectre, but what you are describing really does look like a permutation of https://github.com/redplanetlabs/specter/wiki/List-of-Macros#multi-transform

1
lilactown17:08:47

the selection portion of it looks a lot like using EQL on maps

markaddleman18:08:19

This is very straightforward in meander. Unfortunately, I don’t have time right now to provide sample but the folks in #meander are super friendly

Ben Sless20:08:26

The meander expression would look something like:

(match m
  {:foo ?foo
   :bar {:a ?a
         :b ?b
         :c [?c & ?rest]}}
  {:foo ?foo
   :bar {:a ?a :b ?b :c ?c}
   :bazz (app inc (app count ?rest))})

escherize23:08:21

Having extract-ops as a map will not preserve the order of those operations. otherwise looks kind of like a reduce if I blur my eyes a little

Yogesvara Das07:08:41

While meander, spectre, reduce et al have the functionality they don't have the declarativeness. And order of ops doesn't matter for my use case.

Ben Sless08:08:38

Not picking nay sides here, but isn't meander more declarative?

Yogesvara Das08:08:45

it's more verbose if you don't need the entire map in context at once

Yogesvara Das08:08:28

I think it's also not as compos able because it's a macro? Which would mean you need to have it in a match.

Ben Sless08:08:01

Not sure I follow regarding needing the entire map in context

Ben Sless08:08:46

And yes, it's a macro, but iirc you can splice defined data at call site. The other side of the coin is it has a pretty good compiler

Yogesvara Das08:08:40

Everything in context being like ?a and ?b available when creating the result