Fork me on GitHub
#beginners
<
2023-09-24
>
λ01:09:27

Do I need an LSP if I have clojure-mode and paredit installed in emacs? IF so what is the recommended one?

ghadi02:09:21

It is not essential but many choose to install LSP facilities. I do not. It is, however, essential to have a REPL connection in your editor. I would use CIDER if you’re on emacs

3
ericdallo03:09:09

If you wanna try LSP, I suggest you use lsp-mode, using clojure-lsp as server Check here all features https://clojure-lsp.io/features/

λ19:09:27

Thanks, that’s exactly what I am using

Ingy döt Net02:09:24

Why can one not add metadata to any value?

user=> ^:foo "bar"
Syntax error reading source at (REPL:1:12).
Metadata can only be applied to IMetas

Jason Bullers02:09:31

Someone else might have a more definitive answer for you, but I would assume because values like "bar" or 5 are Java types (String and Long or primitive long) and there's no facility in Java to attach metadata to anything.

Jason Bullers02:09:16

Clojure values like vectors are Clojure defined types, so they can support it

lread02:09:18

Maybe https://clojure.org/reference/metadata#_metadata_reader_macros helps? > Note that metadata reader macros are applied at read-time, not at evaluation-time, and can only be used with values that support metadata, like symbols, vars, collections, sequences, namespaces, refs, atoms, agents, etc. Some important exceptions that don’t support metadata are strings, numbers, booleans, Java objects, keywords (these are cached and can be shared within the runtime), and deftypes (unless they explicitly implement clojure.lang.IMeta).

👍 2
Ingy döt Net02:09:56

Well they seem to need to be instances of classes that implement IMeta (inferring from error message)

Ingy döt Net02:09:55

so that makes sense, though I'm pretty sure one could sort out a way to associate metadata with any object

lread02:09:13

user=> (type "bar")
java.lang.String
user=> (instance? clojure.lang.IMeta "bar")
false
user=> (instance? clojure.lang.IMeta :bar)
false
user=> (instance? clojure.lang.IMeta 'bar)
true
user=> (instance? clojure.lang.IMeta [])
true

lread02:09:48

I agree that it is frustrating that you cannot slap metadata on anything!

Ingy döt Net02:09:50

user=> (with-meta "bar" {:foo true})
Execution error (ClassCastException) at user/eval7634 (REPL:1).
class java.lang.String cannot be cast to class clojure.lang.IObj (java.lang.String is in module java.base of loader 'bootstrap'; clojure.lang.IObj is in unnamed module of loader 'bootstrap')

Ingy döt Net02:09:36

applied at eval time. different error

lread02:09:57

Ya, the blurb I quoted was talking about the reader. But the same rules apply to with-meta.

Ingy döt Net02:09:59

@UE21H2HHD what's up with

user=> (meta ^:foo {})
{:foo true}
user=> (meta ^:foo 'xyz)
nil

Ingy döt Net02:09:05

given your:

user=> (instance? clojure.lang.IMeta 'bar)
true

Ingy döt Net02:09:02

I could make use of metadata on symbols, and it doesn't error, but how to retrieve it?

lread02:09:03

user=> (meta (with-meta 'xyz {:foo true}))
{:foo true}

Ingy döt Net02:09:25

unfortunately that defeats my use case 🙂

lread02:09:43

Not sure about your example above, someone else will surely answer but...

user=> (binding [*print-meta* true] (-> "^:foo 'x" read-string pr))
^{:foo true} (quote x)nil
This old man must now wind down for the day!

Ingy döt Net02:09:58

g'night. and thanks for your time

lread02:09:22

Sure thing, love your energy and curiosity!

Ingy döt Net13:09:23

Is there an easy way to determine equality of 2 values and their metadata (recursively)?

teodorlu14:09:47

Perhaps convert to normal value you can compare with =, then compare with =? It’s probably possible to write something that’s more computationally effective than what I wrote here.

Alex Miller (Clojure team)14:09:36

If metadata affects equality, then it shouldn’t be metadata

1
🎯 1
Ingy döt Net14:09:22

@U3X7174KS > Perhaps convert to normal value you can compare with =, then compare with =? That's the conclusion I came to. Thanks for the code. I'll compare to what I come up with.

🙌 1
delaguardo14:09:40

do you mind sharing what for you need this? it is very rare when you have to include metadata into equality checks

Ingy döt Net14:09:44

@U064X3EF3 That might be a good general rule, but I think its a bit presumptuous to state it absolutely. I'm working out a design for a various AST states for a compiler I'm writing. There's a lot of data involved but not all of it is important all the time. Things like source positions. These things are nice to hide as metadata, and clojure does the same. But for testing, you sometimes need to check the hidden things. I have a data driven test setup that is a very clean way to define tests, and thus I would like to figure this out.

Alex Miller (Clojure team)14:09:22

It’s the whole premise of metadata, by definition

Alex Miller (Clojure team)14:09:45

But understood for testing

Ingy döt Net14:09:09

Right, I only intend to use this for the testing.

Ingy döt Net14:09:59

Probably the best answer for my question here is "No" 😄

Ingy döt Net15:09:26

@U3X7174KS looks like you don't need the IMeta check 🙂

user=> (some? (meta "not IMeta"))
false

Ingy döt Net15:09:04

@U3X7174KS why do you use the ::value and ::meta instead of :value and :meta?

teodorlu15:09:50

When I only want to convert from normal metadata to an “expanded form” like this — it probably wouldn’t matter. But if I wanted to convert back from my representation to normal clojure data and metadata? Then I wouldn’t want to accidentally convert a map someone else wrote with {:value 3 :meta "no!"} to metadata — as that would give me a different result than what I started with. Instead, I wanted to use namespace qualified keys, to avoid collisions with user code. Possibly premature abstraction 🙂

delaguardo15:09:54

btw, you don't need to keep value. it will be verified with regular equality check. just extract metadata recursively and compare

Ingy döt Net15:09:45

but then I need 2 different compares? one for value and one for meta?

Ingy döt Net15:09:07

just trying to understand what you are suggesting

delaguardo15:09:16

yes, two checks.

Ingy döt Net15:09:29

that might actually work well for me

teodorlu15:09:25

> btw, you don’t need to keep value. it will be verified with regular equality check. just extract metadata recursively and compare what if the value has metadata somewhere in it? Would that metadata be checked for equality then?

Ingy döt Net15:09:33

I define my tests in a yaml file (a seq of maps) and each map has a bunch of data keys: '(:a :b :c :d :e :f :g) Each mapping can define all of those keys or just some of them. A given test file calls a loader function the takes: • the path of the yaml file • a need-keys vec of keys • a test-func function • a want-func function that produces the expected result The loader function loads the yaml seq and filters out the maps that contain all the need-keys. Then it defines a test function for each map that calls:

(= (want-func test-map) (test-func test-map))
Generally for a given file I am using a need-keys of just 2 of the many values. One for input and one for expected output. The compiler I'm writing has several stages that I need to test. This whole technique lets me define the test data very cleanly.

Ingy döt Net15:09:59

This pattern is so common for me that I'll publish this as a reusable library later on.

Ingy döt Net15:09:49

@U04V4KLKC mixing the metadata with the actual data is messy so I like the idea of separating it, and only using it on some of the tests.

delaguardo15:09:37

I would make a function similar to prewalk with an additional argument - the path suitable for get-in. for every value capable to carry meta it would compare value's meta with (meta (get-in other-obj path)). it can even shortcircuit on not identical metadata

Ingy döt Net16:09:51

@U3X7174KS I see your point:

(all-meta ^:x {:y ^:z [3]}) => {:x true}
I think if I do want the meta only I would need a second pass to prune stuff

teodorlu16:09:32

yeah, that ^:z [3] is the sort of thing I thought might get lost. I think delguardo’s approach might work (and I think it’ll be faster than my code), but I don’t know how I would start writing it.

teodorlu16:09:11

It might be possible to prune values that have no metadata from the bottom up. clojure.walk/postwalk gives you leaf nodes first, then nodes with children afterwards. I tried something like this: 1. Replace all leaf nodes that have no metadata with a ::prune instruction (from {::value v} to ::prune) 2. When you later encounter a value where ::meta is missing and ::value is ::prune, replace the value itself with ::prune. But now my head is spinning from all the recursion so I’m the problem down for now 😅 Hope you find something that works! Being able to follow your progress om yamlscript and lingy here on the slack is a treat.

Ingy döt Net16:09:22

So far I have

(defn show-meta [node & ignore]
  (walk/prewalk
    (fn [value]
      (let [meta (apply dissoc (meta value) ignore)]
        (if (some? meta)
          {:M meta
           :V (with-meta value nil)}
          value)))
    node))
basically yours with the feature to ignore certain meta keys
=> (show-meta ^:x {:y ^:z [3]})
{:M {:x true}, :V {:y {:M {:z true}, :V [3]}}}
thinking about what the prune would look like:
[{:x true} {_ [{:z true} [_]]}]
maybe

👍 1
Ingy döt Net16:09:43

Not sure I could readily read and understand that. Might be best not to separate.

Ingy döt Net14:09:17

user=> (case (type []) (type []) 42)
Execution error (IllegalArgumentException) at user/eval7706 (REPL:1).
No matching clause: class clojure.lang.PersistentVector
why?

Alex Miller (Clojure team)14:09:19

Case values need to be compile time constant

Alex Miller (Clojure team)14:09:41

Classes are not constants

Alex Miller (Clojure team)15:09:10

And the case value is not evaluated

Alex Miller (Clojure team)15:09:34

If you take the name of the the class in the expression, you can match that

1
Ingy döt Net15:09:45

I also tried

user=> (case clojure.lang.PersistentVector clojure.lang.PersistentVector 42)
Execution error (IllegalArgumentException) at user/eval7709 (REPL:1).
No matching clause: class clojure.lang.PersistentVector

Alex Miller (Clojure team)15:09:37

The expression is evaluated here to a clas but the case match is a symbol (not evaluated)

Alex Miller (Clojure team)15:09:24

Depending on the rest of the context, cond with instance? is maybe better

Alex Miller (Clojure team)15:09:19

There are vectors that are not PersistentVector

Ingy döt Net15:09:43

I was basically writing a walk because I wasn't familiar with clojure.walk but I'm looking at that now.

phill10:09:03

@U05H8N9V0HZ a fascinating thing is that in your case the second (type []) is interpreted as TWO cases that share an outcome. The first of the two is type and the second is []. This is documented at https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/case clj꞉user꞉> (case 'type (type []) 42) 42 clj꞉user꞉> (case [] (type []) 43) 43

Ingy döt Net15:09:21

Fascinating indeed. >

Note that since
> lists are used to group multiple constants that map to the same
> expression, a vector can be used to match a list if needed.
that part I assume.

phill23:09:53

Perhaps we can trace case's quirks, (1) unevaluated datums and (2) lists of datums, to Scheme's case https://docs.racket-lang.org/reference/case.html

Ingy döt Net23:09:51

I wonder which Lisps Rich looked at most...

Ingy döt Net23:09:15

Probably in his History of Clojure paper...

maksut15:09:44

Is there a way to call java methods that use "the signature-polymorphic parameter list"? https://stackoverflow.com/questions/52378078/using-java-lang-invoke-methodhandle-in-clojure`MethodHandle#invokeExact`https://stackoverflow.com/questions/52378078/using-java-lang-invoke-methodhandle-in-clojure. I've been playing with new foreign function and memory api. The api makes use of such functions. Another example is methods of java.lang.invoke.VarHandle. It would be nice to use the api without falling back to java.

ghadi15:09:36

there is not support to do this in Clojure

maksut15:09:01

Looks like I'll have fallback to java. Thanks.

maksut15:09:52

@U7RJTCH6J that looks cool! Thanks.

maksut15:09:49

I'll try to steal that. I might just end up using coffi.

👍 1
vlad_poh16:09:11

Saw a question on this Kahoot https://create.kahoot.it/share/clojurebridge-quiz/f45a89b7-b5f3-4c42-a655-ac8a6b9dcb24 How many different function calls are made in

(/(reduce + (map :angle a)) (count a))
Why is 4 not the answer

dpsutton16:09:10

can you enumerate the 4 you think there are?

dpsutton16:09:57

(also, number of function calls can be pretty ambiguous. (/ 3 4) could have quite a few different number of function calls depending on how familiar you are with Clojure internals, perhaps how familiar you are with jvm internals

vlad_poh16:09:18

Just these 4 / reduce map count

dpsutton16:09:52

map takes a function and applies it to a collection for each element in the collection right? So it calls :angle as a function for each item in the a collection

hiredman16:09:56

It also depends on if you mean the number of calls present in the syntax, or the number of calls that will be executed when the code is run

dpsutton16:09:12

but it also creates a collection of the results and i don’t know off hand how many function calls that would take

vlad_poh16:09:22

They said the answer is 6

hiredman16:09:30

It could also just be a poorly worded question

👍 1
hiredman16:09:58

Yeah likely it should be something like "how many values are being used as functions?"

☝️ 1
oddsor08:09:54

Since the question was > How many different function calls… I counted 6: • / • reduce • + • map • :angle • count Like @U11BV7MTK says, map calls :angle as a function. I guess in my head I just automatically counted the second argument to map as a function 😅 (same with reduce’s second argument) Though that’s just a surface level counting that doesn’t take into account whatever happens behind the scenes!

Ingy döt Net17:09:08

Is there a simpler way:

user=> (def ks [:a :b])
#'user/ks
user=> (def ms [{:a 1 :b 1} {:a 1 :c 1} {:a 1 :b 1 :c 1}])
#'user/ms
user=> (filter #(every? (fn [k] (k %)) ks) ms)
({:a 1, :b 1} {:a 1, :b 1, :c 1})

andy.fingerhut17:09:43

I would not be surprised if someone else comes up with a simpler way, but you can't be too far from it with code that short.

andy.fingerhut17:09:16

I only wanted to comment that I personally think it is worth naming a function like that something like has-all-keys? or contains-all-keys?

👍 1
andy.fingerhut17:09:10

Then you could write (filter #(has-all-keys? ks %) ms)

Ingy döt Net17:09:39

Yeah. Just seems like a core need 🙂

Ingy döt Net17:09:53

> I would not be surprised if someone else comes up with a simpler way, but you can't be too far from it with code that short. /me waits for @U04V70XH6 😄

hiredman17:09:31

That's not actually has-all-keys, because it is invoking functions, not doing s contains key test, not all keys can be called as functions, and not all values are truthy

dvingo17:09:38

another version, not any shorter though

(filter (comp (partial clojure.set/subset? (set ks)) set keys) ms)

hiredman17:09:52

You could apply every-pred or whatever that ended up being called and then call the result if that non-generalness is acceptable

hiredman17:09:44

Also the (fn [k] (k %)) in this case has the same behavior as just %

Ingy döt Net17:09:22

(filter #(every? % ks) ms) will work nicely for me here. thanks.

Ingy döt Net17:09:05

need to get better at spotting those things

hiredman17:09:19

(filter (apply every-pred ks) ms)

dpsutton20:09:56

using truthy can backfire if your meta values ever contain false . may not impact you but can be annoying when that’s a legit value in your metadata that starts failing these predicates

Ingy döt Net20:09:52

I actually had prodlems with the every-pred Changed to using:

(defn has-keys? [keys map]
  (every? map keys))
(filter #(has-keys [:a :b] %) maps) 

dpsutton20:09:39

(has-keys? [:a :b] {:a 1 :b false})
false

dpsutton20:09:57

^ that’s the issue i’m pointing out. you’re checking that a key is present by finding it’s truthy value

dpsutton20:09:47

also up to you if you want to consider nil to a key as “present”

(has-keys? [:a :b] {:a 1 :b nil})
false
again, just pointing out that it’s up to you how you want to consider this and your impl currently already has an opinion on it but it’s easy to miss

dpsutton20:09:19

contains? and find will both look for the key or key/value in the map regardless of the value

Ingy döt Net20:09:41

sounds like a yes

👍 1
Ingy döt Net20:09:29

(defn has-keys? [keys map]
  (every? #(contains? map %) keys))
I probably don't have falsy vals, but I'd rather do it more correctly.

dpsutton20:09:12

they can spring up in funny ways :pure false :io? false or if you track some dependencies it might be convenient to have the set be nil or a set with items, etc

1
Ingy döt Net20:09:04

Thanks for your time. Appreciated!

👍 1
Felix Dorner20:09:39

How can I convert a qualified keyword to an unqualified one, i.e. just use the unqualified part of it?

Felix Dorner20:09:53

(keyword (name :clojure.core/bla))
maybe?

2
seancorfield20:09:02

Curious why you need to? Conversion to JSON? All the JSON libs have a way to do this automatically (with some it is the default).

Felix Dorner20:09:41

Uhm, I’m trying to write a one pass run over an xml event sequence using clojure.data.xml where start elements have a :tag that is a qualified keyword where the namespace of the tag is the url encoded value of the corresponding xml namespace. I want to drop that qualifier and just get the tag w/o namespace.

seancorfield20:09:52

I still don't understand why you need to throw the namespace portion away?

seancorfield20:09:16

Are you trying to identify a "similar" tag across multiple namespaces?

Felix Dorner20:09:41

Nope. In my case the qualifiers are just noise that I can get rid of, as there’s no collisions.

Felix Dorner20:09:55

I’m still not 100% sure what I want, but atm the plan is to define a function for each tag I’m interested in, and then dispatch to that function whenever the tag is found.

Felix Dorner21:09:47

If I want to keep the qualifiers, and I have :superlong-url-escaped-namespace-qualifier/myTag I wouldnt know if I can create a clj file for that namespace so that clojure would find it where it’s expected.

1
adi01:09:08

> atm the plan is to define a function for each tag I’m interested in, and then dispatch to that function whenever the tag is found. Sounds like a job for multimethods?

Felix Dorner17:09:37

Just read about multimethods, nice, yes that can be a fit I guess. Thanks!

👍 1