Fork me on GitHub
#beginners
<
2022-06-15
>
Sturm00:06:00

Is it common to use a question mark on a binding to a boolean like this, or is "?" really only for predicate functions?

(let [available? (get-in data [:a :b :c])
  ...
)

dorab00:06:18

I don't know if there is any particular common or recommended usage. I would say that what you show is perfectly reasonable usage.

seancorfield00:06:44

It's common for predicate functions (returning strictly Boolean results) but there's no a lot of consensus for non-function names. Some people like ? for Boolean-valued bindings, some don't.

seancorfield00:06:36

I would say it's probably best to use it only for Booleans, not arbitrary truthy/falsey data -- so in the case you are asking about, if :c's value is strictly true or false, it's probably OK. But I mostly don't use ? in local symbol names, preferring to rely on truthy/falsey, and nil-punning in the general context.

👍 1
seancorfield00:06:44

For example, I'm more likely to do:

(if-let [v (get-in data [:a :b :c])]
  ... do stuff with v (available) ...
  ... handle not available case ...)

seancorfield00:06:12

(assuming you don't care to differentiate false and nil/missing there!)

seancorfield00:06:59

if-some can be useful when you need to handle nil and false separately, BTW.

👍 1
Loic06:06:22

I am new to the macro part. I want to slurp a md file and turn it into hiccup in my reagent app. However it says my var is undefined and I cannot see why: md_to_hiccup.clj

(ns flybot.lib.md-to-hiccup)

(defmacro md-file->hiccup [resource-path] 
  `(markdown-to-hiccup.core/component
    (markdown-to-hiccup.core/file->hiccup
     ~resource-path)))
md_to_hiccup.cljs
(ns flybot.lib.md-to-hiccup 
  (:require-macros [flybot.lib.md-to-hiccup])
  (:require [markdown-to-hiccup.core]))
blog.cljs
(ns 
  (:require [flybot.lib.md-to-hiccup :as m]))

(def md-files
  ["src/flybot/content/post1.md" "src/flybot/content/post2.md"])

(defn blog-page []
  [:section.container 
   (for [f md-files] 
     (m/md-file->hiccup f))])
The error when I save via figwheel:
[Figwheel:WARNING] Compile Warning   /Users/loicblanchard/workspaces/flybot.sg/src/flybot/pages/blog.cljs   line:12  column:6

  Use of undeclared Var markdown-to-hiccup.core/file->hiccup

   7    ["src/flybot/content/post1.md" "src/flybot/content/post2.md"])
   8  
   9  (defn blog-page []
  10    [:section.container 
  11     (for [f md-files] 
  12       (m/md-file->hiccup f))])
           ^---

[Figwheel] Successfully compiled build dev to "target/public/cljs-out/dev-main.js" in 0.412 seconds.
Technically, it is a warning but the hiccup does not show. Any idea why, even though I fully qualified my ns as highlighted in this good https://code.thheller.com/blog/shadow-cljs/2019/10/12/clojurescript-macros.html , I still have this problem?

Ferdinand Beyer09:06:04

(m/md-file->hiccup f) This is a typical mistake I also tend to make when writing macros. Remember that macros work on the unevaluated forms, so when it is invoked the resource-path will be bound to the symbol f. When you want to evaluate it at macroexpand time, you need to do so yourself using eval. However, this will not work in loops.

Ferdinand Beyer09:06:39

(for [f md-files] 
     (m/md-file->hiccup f))
The for will expand to some runtime code, it will not unroll the loop at compile time. Your macro will not be expanded with the file path. For this to work you would need to unroll yourself, e.g.
(m/md-file->hiccup "src/flybot/content/post1.md")
(m/md-file->hiccup "src/flybot/content/post2.md")

Ferdinand Beyer09:06:11

Finally, since you want to read the file at compile time, don’t syntax-quote the call to markdown-to-hiccup.core/file->hiccup.

Loic09:06:46

Thank you very much for the answer. yes I noticed calling them one by one was working but it is not good as if I have 20 mds it will become ugly. So basically, I cannot call my macro inside for or map ? That limits drastically the use of clojure macros then no?

Ferdinand Beyer09:06:05

You can, but you always need to understand what macros do.

Ferdinand Beyer09:06:47

In your case you could also make your macro take multiple arguments and loop at macro expansion time

Ferdinand Beyer09:06:51

In general you should try to keep the code in macros super simple. I usually write a normal function that emits Clojure code, usually called emit-something, and call that from my macro. Makes experimenting and testing much easier.

Loic03:06:44

I see, at least now it is clearer. Basically I cannot use syntax quoting because I want to execute the macro at compile time and not run time. I cannot call my macro inside the loop because the loop is at runtime only and so my form cannot be evaluated by my macro. I made it work calling the macro multiple times as you suggested. I guess I cannot really achieve what I want this way so I'll check other solution.

kiranshila16:06:52

And process markdown in the frontend - it's reasonably fast

Sohang Chopra07:06:16

Hi everyone! I just joined this Slack channel. 🙂 I have been using Calva extension (in VS Code) for learning Clojure - it's quite good! I am starting to make a TUI program - but I can't figure out how to show the running TUI app in a seperate terminal from the Calva Output REPL. Basically, I want to run a TUI program, while still being able to eval expressions in Calva Output REPL. I checked out this page: https://docs.cider.mx/cider/basics/middleware_setup.html , but I didn't really understand it. Can anyone help me out with this? Any pointers?

pez07:06:34

Hi there! Glad you like #calva. The Calva Output window is not a terminal, so I am not quite following. Can you elaborate a bit on what workflow it is you want to achieve?

plexus08:06:57

I think I know what you're after @U03L31E1Y1F, I think you have two options: • embed an nREPL into your program, then start it from the terminal, and connect to it from Calva • embed a tcp server in your app, start the app from Calva, then connect from a terminal with nc or telnet (this is what I used to do when working on this kind of stuff)

plexus08:06:17

the first option is probably easier, something like

(ns my-app
  (:require [nrepl.server :as nrepl-server]))

(nrepl-server/start-server :port 7889)

plexus08:06:46

but Calva probably requires some nREPL middleware to have all its functionality. I believe it also uses the CIDER middleware but not sure, @U0ETXRFEW can elaborate on that. In that case this snippet that you linked to should work: https://docs.cider.mx/cider/basics/middleware_setup.html#using-embedded-nrepl-server

plexus08:06:19

run that somewhere when your application starts, then from Calva try connecting to that nREPL port.

pez08:06:14

Yes, Calva assumes cider-nrepl middleware.

pez15:06:27

So, @U03L31E1Y1F and I had a chat and I think I understand the problem better now. It is that the nrepl server captures stdout and sends it to Calva's nrepl client. We'll need to figure out how to stop that. Seems like it should be a common problem which should have solutions. Is this how you understood the problem as well, @U07FP7QJ0?

Sohang Chopra16:06:00

@U0ETXRFEW One thing to add here - Direct evaluation results of expressions SHOULD show up in Calva's Output, but normal stdout should not be captured by Calva

pez16:06:10

Yes, good that you mention. Results should keep going to the client.

pez16:06:55

Interestingly enough, when I google this, all I find is people wanting output to be sent to the nrepl client. So the opposite of what you have @U03L31E1Y1F.

Sohang Chopra16:06:56

@U0ETXRFEW I'm sure other people must also have wanted something like this - perhaps we simply need to refine our Googling skills 😀

pavlosmelissinos14:06:38

Is there a more idiomatic way to find if two sets have common elements than (not-empty (set/intersection s1 s2)) or (seq (set/intersection s1 s2))? I need it to return logical true/false but I don't care if the result is actually a boolean or not.

ghadi14:06:02

I like the (seq (intersection ..)) . That's very clear. Another thing you might consider is (some s1 s2) :

user=> (some #{:a :b :c} #{:c :d :e})
:c

💡 1
pavlosmelissinos14:06:07

oooohhh, some is such a cool idiom, I generally reach for it quite often but didn't even consider it in this case 🙂 > does not work if false or nil are valid members of your sets they're not and will never be, so some will do, thanks a lot for the suggestion 🙂

ghadi14:06:12

that variant uses a set as a membership function, and will short-circuit on a match. It does not work if false or nil are valid members of your sets

dpsutton14:06:37

One thing to think about. I think the (some #{} the-set) is really nice. But I cannot imagine how you get more idiomatic than code that is (not-empty (intersection set-1 set-2)). That has got to be some of the most readable code of what you want to accomplish. Perhaps chasing idioms isn’t always a great strategy if it causes you to doubt that extremely readable code.

🙌 1
👍 3
pavlosmelissinos14:06:37

That's definitely a good point and I don't really have a strong opinion. I think it depends on who is going to read your code. Most Clojure developers will recognize certain idioms like (seq coll) to check if a collection is not empty/nil but on the other hand someone who is new to Clojure will prefer a version that will feel foreign to us (e.g. (not (empty? coll))). Which one is better? If you take the idiom argument from the equation, the latter is probably more readable but the fact is I don't use it and neither does anyone I know.

jumar15:06:18

I use the latter and I think it’s preferable in almost all cases unless you actually want to do something with that seq result

pavlosmelissinos16:06:45

That comes as a bit of a surprise, I thought seq was preferred in the general Clojure community. In fact I picked this particular example because it's even in https://github.com/clojure/clojure/blob/38bafca9e76cd6625d8dce5fb6d16b87845c8b9d/src/clj/clojure/core.clj#L6208 of empty? 😄

jumar16:06:15

I know but I dont share that sentiment. Tonsky had a similar opinion.

👍 1
pavlosmelissinos16:06:17

That's... liberating to be honest, thanks :)

mbjarland09:06:11

out of curiosity, why is it considered better to do (seq x) than (not (empty? x))?

jumar09:06:22

I have no idea - because it's shorter?

mbjarland09:06:31

and yes I hear that there are participants here that do not share that sentiment but I would assume there is some argument behind that docstring whether or not one agrees with it

mbjarland09:06:40

hmm…that seems a tad weak

jumar09:06:42

If you actually use the result then it makes sense, like

(when-let [xs (seq x)]
  .. do something with xs)
This can be useful with recursive functions too. But I wouldn't use it for simple checks like
(if (seq xs)
.. do something completely different

mbjarland09:06:08

so empty? seems to do (not (seq coll)) which means (not (empty? coll)) would expand to (not (not (seq coll))) , maybe it’s the inefficiency and redundancy of (not (not …)) that caused that docstring

mbjarland09:06:10

but I would agree, if that is the argument I would rather be infinitesimally inefficient and more readable

jumar09:06:32

There was actually a discussion last year about this same stuff that I remember and could look up 🙂 https://clojurians.slack.com/archives/C03S1KBA2/p1559678425010600 Alex offered a good advice there which I vaguely remembered and tried to mention one part of that above. https://clojurians.slack.com/archives/C03S1KBA2/p1559679354021900 > • if you are working with collections (not sequences), forcing a seq just to check emptiness is not efficient > • the advice to use (seq x) as a terminating predicate is primarily of use when working with sequences, where you are going to force the sequence anyways to walk it (and sequences cache that so the additive cost is negligible)

Benjamin15:06:11

(def launch-date #inst "2021-05-04")
do you put reader tags in source code or is that wierd?

dpsutton15:06:37

reader tags are fine. One thing to think about if that is an acceptable data type for a date. It expands to #inst "2021-05-04T00:00:00.000-00:00" Java time has some date classes that might be better for your purposes

henryw37409:06:27

I avoid reader tags in source bc a feature of them is that the reader functions for them can be changed - and if they are the source will most likely break (e.g. if #inst is read as something other than java.util.Date).

emilaasa15:06:19

Let's say I have a function f that looks something like this:

(defn f [a-seq b-val]
  (if (seq a-seq)
    [b-val]
    (g b))
If I have something in the a-seq I want to return b as a sequence, because my consumer of f expects that. If I don't have anything in a-seq I use b to generate another sequence. I went with wrapping b in [] but I find it a bit hard to read. How would you do that?

dpsutton15:06:54

i take it g is some other function. What is b?

dpsutton15:06:56

is you question the best way to return a sequence of a single value? If so, [x] and (list x) both seem just fine to me

emilaasa15:06:52

Yeah in this case I think I should have used better examples - I'm working with dates at the moment 🙂

emilaasa15:06:22

I'll use list that makes it easier for me to see at least, thanks! ❤️

👍 1
Yuriy Zaytsev15:06:36

(defn f [a-seq b]
  ((if (seq a-seq) list g) b))

JaimeV16:06:11

Any recommendations on how to query OpenAPI schemas?

dorab18:06:15

Can you provide some examples of the kinds of queries you are looking for? You might look at https://github.com/oliyh/martian , which, among other things, transforms an OpenAPI spec into a Clojure data structure. Which can then be operated upon or searched.