Fork me on GitHub
#beginners
<
2023-02-24
>
fenton05:02:20

Quick instructions on setting up a project for logging with logback and SLF4j (everything all in one place in 82 words (- xml file config :)). https://github.com/ftravers/dotfiles/blob/master/homedir/org-roam/clojure.md#logback--slf4j

Chase14:02:14

I'm getting a page not found error. Are you sure this repo isn't private?

fenton12:03:48

oops, sorry, fixed that...can you try again? @U9J50BY4C

Chase15:03:55

Cool, thanks. I'm bookmarking as I don't actually need it at the moment. Also browsing through the rest of your Clojure notes haha

đź‘Ť 2
slk50011:02:49

what is the best way to create html table with row numbers in clojurescript hiccup? I have found map-indexed function:

(defn table-with-row-numbers [videos]
  [:table
   [:thead
    [:tr
     [:th "Row Number"]
     [:th "Video Title"]]]
   [:tbody
    (map-indexed (fn [i video]
                   [:tr
                    [:td (inc i)]
                    [:td (:title video)]])
                 videos)]])
or maybe don't even start with counters in 'for'' and add row number in CSS?

practicalli-johnny16:02:37

I use for to create html structure (div rather than table) and pass a hash-map of data with the values used in that structure the generator functions in the Practical landing page use this approach If numbering is important it could be added to the data passed into the function and if necessary the data sorted by the number value before used in for. (not suggesting this is best, as best is relative to what best actually means and possibly the significance of the numbers https://github.com/practicalli/practicalli.github.io/blob/live/src/practicalli/books.cljs

đź‘Ť 2
phill17:02:58

I have found map-indexed is super useful with React because React wants each item in a list to have a distinct key. Of course keys representative of their item are most likely to help React avoid work if you make certain kinds of updates to the list, but a sequential counter is a convenient second-best. I.e., [:tr {:key (str i)} ...

đź‘Ť 2
slk50017:02:11

thank you guys for your opinion. I would stick with map-indexed

Thanh Nguyen13:02:43

Hey guys. Is there a rule of thumb for when should I pass a full map versus when should I pass single parameters? For example, I have this kind of state

(def sample-state
  {:head [0 0]
   :tail [0 2]})
Then I am not sure when should I write my function like this:
(defn f [head tail])
Or like this
(defn f [state])
For more context: I am solving Advent of Code Day 9, where there is head and tail moving on a 2D map. I figured out that I should have a state that represent the two variables, and multiple functions to determine additional information or advance the state
.....    .....    .....
.TH.. -> .T.H. -> ..TH.
.....    .....    .....

...    ...    ...
.T.    .T.    ...
.H. -> ... -> .T.
...    .H.    .H.
...    ...    ... 

kennytilton13:02:35

What are the semantics of function f? Indeed, what is the real name of f? And what is the real name of sample-state? I do not think Clojure itself dictates any answer, the API being built decides that. So if f expects a list and knows about how you are encoding that, you can pass sample-state. If f is ... well, if (defn f [state] ...) is even an option, it must know about the data structure. So I would pass that rather than pull it apart and create a point of brittleness should the state structure have to change. my2 🤷

đź‘€ 2
delaguardo13:02:39

> "It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." This quote is from Alan Perlis' Epigrams on Programming (1982). So I usually peek one function form that meets my current needs best. And if I need another later, I can add one more function.

(defn f [head tail])

(defn f* [{:keys [head tail]}]
  (f head tail))

đź‘€ 2
ajones13:02:27

to the point of the question “what is the purpose of the function”, another way to think of it is in term of the architectural layers. a representing pattern being, 3 categories of functions, one that operates on scalar values, the next level up (that may or may not use the previous layer) operating on entities (or rather maps), the final being usually an aggregation point but that operates on sequences. so, the intent of the function, driving the type of parameter list it accepts.

đź‘Ť 2
đź‘Ś 2
ajones13:02:57

order of parameters is also an important consideration, which can heavily influence the ergonomics of use later when using compositional mechanics like partial, comp, ->, transducers, etc. rarely going to be perfect on the first pass, but important to pay attention and try to get in a decent shape up front. from experience, in the long term in massive code bases, there can be a lot of pain/confusion until these considerations are addressed.

Thanh Nguyen14:02:39

@U0PUGPSFR @U04V4KLKC @UE4SAM0A0 thanks a lot for your answers. I updated the question for more context, but I think I had my answer: in this case, just pass a map since it is going to be simpler to use with reduce

đź‘Ť 2
ajones14:02:32

cool, np đź‘Ť

practicalli-johnny17:02:58

My only rule of thumb is to define functions that take a hash-map as an argument unless there is on obvious need not too I like to give my values context regarding their purpose as it makes the code easier to work with A hash-map also give a lot of flexibility as to what information can be received and allows the API of the code to grow with less chance of breaking changes or creating similar functions

🙌 2
cddr17:02:14

I’m trying invoke clojure while setting a system property and don’t seem to have quite the right syntax. I’ve tried

clojure -M -m my.main.ns -J 'Dmy.main.props=test/resources/cfg.properties' 
clojure -J 'Dmy.main.props=test/resources/cfg.properties' -M -m my.main.ns 
and a few other variations thereof but nothing seems to set the property

Alex Miller (Clojure team)17:02:31

clojure -J-Dmy.main.props=test/resources/cfg.properties -M -m my.main.ns

cddr17:02:44

Ah cool. Thanks alex

dpsutton17:02:56

it’s in clj --help > clj-opts: > -Jopt Pass opt through in java_opts, ex: -J-Xmx512m (i always go back and reference this so letting you know where you can find it)

mathpunk18:02:14

Someday I will cat something that I spit and not get clojure.lang.LazySeq@8b461e25 but today is not that day. What’s the correct version of my incorrect

(spit "midnight-fails.txt" (doall (for [[scenario message] (apply concat (brief-failure-info failures))]
                                     {:scenario scenario
                                      :message message})))
?

mathpunk18:02:10

bonus points for being pretty-printed

phronmophobic18:02:47

have you tried clojure.pprint/pprint?

phronmophobic18:02:48

usually, if you're writing edn, then you should use some variant of clojure.pprint/pprint , pr, or pr-str.

phronmophobic18:02:00

with the caveat that writing readable edn reliably is tricky and at the very least, want to explicitly set the various bindings that control printing. Here's an example:

(defn write-edn [w obj]
  (binding [*print-length* nil
            *print-level* nil
            *print-dup* false
            *print-meta* false
            *print-readably* true

            ;; namespaced maps not part of edn spec
            *print-namespace-maps* false

            *out* w]
    (pr obj)))

mathpunk18:02:10

I’ve used pprint in the past, I’m just always thrown by some missing understanding around how side effects and lazy sequences interact

mathpunk18:02:33

I thought that if I chucked a doall in there, it would make everything evaluate, and only then would the spit occur

phronmophobic18:02:39

> I thought that if I chucked a doall in there, it would make everything evaluate, and only then would the spit occur That is what is happening, but calling spit calls str on the content. The .toString method for for 's return value is always clojure.lang.LazySeq@... regardless of whether it has been realized.

phronmophobic18:02:56

> (str (doall
        (for [i (range 3)]
          i)))
"clojure.lang.LazySeq@7480"

mathpunk19:02:18

How do I apply your write-edn function to get a plain text file?

mathpunk19:02:23

(I don’t mean, as distinct from edn…. I’m just trying to get data to paste to someone who isn’t running a Clojure notebook)

phronmophobic19:02:24

something like:

(require '[ :as io])
(with-open [w (io/writer "my-file.edn")]
  (write-edn w my-data))

mathpunk19:02:52

thank you!!

mathpunk19:02:29

is the secret using real writers, then?

phronmophobic19:02:05

you can also use spit with pr-str

phronmophobic19:02:36

I only use spit as a quick and dirty option. Usually, using the stuff is a similar amount of code and is easier to tweak to fit the use case.

1
1
phronmophobic19:02:00

same goes for slurp.

madstap19:02:17

you can also just put it in a vector using vec instead of doall

1