Fork me on GitHub
#beginners
<
2022-10-17
>
Chris11:10:35

I was on a flaky connection yesterday trying to get dependencies and had to force stop the process, when I’m starting my intellij deps.edn project again now I’m getting this error:

The following errors were found during project resolve: /Users/user/projects/clerk-demo/deps.edn: Could not acquire write lock for 'artifact:org.bytedeco:mkl:2020.3-1.5.4'
Is there a lockfile I can delete or does this indicate some other issue?

rolt12:10:27

_clj_ -Spath should be in .m2 or .gitlibs

Alex Miller (Clojure team)12:10:55

looks like .m2, not .gitlibs

Chris12:10:29

it’s .m2, yes. I tried deleting the mkl path but still get the same error

Alex Miller (Clojure team)12:10:37

you might try going one directory back to get any other lock files in the dir

Alex Miller (Clojure team)12:10:58

~/.m2/repository/org/bytedeco/mkl or something

Chris13:10:33

there is only a 2020.3-1.5.4 folder after running -spath again, no other file

Chris13:10:55

I think maybe the issue is my intellij project that throws the error

Chris13:10:17

-spath downloaded some more files and I can just run clj in the directory

Chris13:10:44

ok seems to work now, not sure what the issue was

Marius12:10:04

I’m currently refactoring and changing the structure of a (rather large) nested map, which I use to collect payroll calculation parameters and results, being used across multiple functions. In a statically typed language, I could just move the field (getters/setters) and would be notified of all necessary fixed. In Clojure, I feel a bit insecure if I updated all references. I am sure there is a better way? It feels a bit fragile to have get-in calls with the paths and no validation for typos. Some ideas I have are • Writing small getters/setters functions, which would contain an get-in/assoc-in an be the only place to keep track of map structure - feels easy at first, but gets quickly out of hand and is one more abstraction layer to maintain instead of a simple map • defrecord seems to be a better choice instead of a bare map, but I don’t gain much for the “type safety” I am looking for during refactoring • Adding pre-conditions to all functions accessing the map, to make sure the key exists - I think this is generally a good idea, then I would catch refactoring issues during runtime - anyone disagreeing? • What else?

1
Marius14:10:38

That would be like a :pre condition on the function, except more concise, right?

walterl14:10:10

Yes, but more purpose built, and well thought out. You also get some extras, like generative testing and instrumenting spec'ed functions only in certain (dev/test) environments.

pppaul15:10:39

for validating structure and simple types, i have found malli very quick and simple. the schemas in malli look pretty close to the data they are validating. however, could you go into more detail about how you are refactoring? i use clojure to transform arbitrarily nested trees, where i actually don't know what parts of the top nodes look like, but i do know a bit about what the middle nodes look like, and sometimes i don't even know what the leaf nodes look like. so maybe i can be some help with techniques i have found helpful

pppaul15:10:33

i don't typically use functions as field getters/setters, as once your tree has duplicate data in it (very common), these functions become context sensitive, and setting data requires changing things in multiple places. it's really common in clojure to process data, keep the old version somewhere close to the processed version (meta, or map key), and you quickly end up with a big mess. it's also common to duplicate data and change a field or two because the meaning is different (similar to making a view classes for different components of the same data)

pppaul15:10:54

for your points, i think i covered #1. #2 there are things about records that make them less convenient to use than maps, and these are a bit rare in clojure now. #3 is basically using asserts everywhere, this is common in debugging, but :pre and :post are pretty bad at telling you what is wrong, using a full blown schema like malli is 100x better because it'll pretty print the exact point where your data mismatches the schema. however i think schemas are more used at the boundary of apps. I think if you trust your code so little, you may be better off at writing some tests against it, as it's easier to convey meaning in those, than in asserts and function schemas

Marius17:10:02

Hey @UJY23QLS1 & @U0LAJQLQ1, thank you very much for your responses! Regarding spec/malli: I am already using malli to validate at the boundaries, and it didn’t seem to be the right tool here. Regarding what I am refactoring: The part I am refactoring is about payroll calculation, for which a map is chained through a couple of functiions: At first the map is enriched with all parameters necessary for payroll (like premiums, employee age calculations, region specific clauses), afterwards the actual calculation takes place, using the the data from the map as input. Now I want to restructure this map and let’s say I don’t have much confidence in my find+replace skills 🙂 I have plenty of unit tests for each function, but the overall end-to-end test is missing yet - my takeaway from this discussion is that this is what I have to fix, instead of runtime checks.

pppaul17:10:31

ok, maybe something like https://github.com/plumatic/plumbing defnk can help you find functions with bad arguments mappings

pppaul17:10:59

i sometimes use these for that exact purpose, and after a while i convert them back to defns

pppaul17:10:45

the error reporting on defnk is very good, the overhead is low, and translation from defn is easy

Marius18:10:49

Very interesting! Thanks a lot!

Marius18:10:13

Btw. they using an assertion in their example, this is another option we haven’t discussed (though similar to spec/pre-condition)

pppaul18:10:11

assert is better than :pre for reporting errors, spec is better than assert, but takes more work. if you need type reporting spec is ok, i find it cumbersome compared to defnk but i don't typically care about types cus clojure will do ok reporting type errors.

sheluchin14:10:07

Does there happen to be some macro that takes a set of symbols and produces a map of those symbols as keywords to their deref'd vars?

sheluchin14:10:56

I find myself doing (let [x 1 y 2] (foo {:x x :y y})) often enough..

sheluchin14:10:55

Good stuff. Glad I asked. Thank you, @UJY23QLS1.

👍 1
hiredman15:10:27

For the example up above there are no vars

sheluchin15:10:09

@U0NCTKEV8 what do you mean?

hiredman15:10:53

Local names like those bound by let don't get vars

sheluchin15:10:41

Ah, I see. So my problem description has a bug, but both of the suggested snippets will work.

sheluchin14:10:32

Is there a version that also works in cljs?

leif17:10:33

I do seem to be able to access it from why webbrowser.

leif17:10:53

mm...Well if it was down, looks like its back up now. Strange.

Abhi Saxena19:10:18

I have a function with atom as an argument which modifies a key if present and formats the value.....how can I rewrite this without passing atom and use just assoc/dissoc -

Abhi Saxena19:10:20

(defn generate-update-input-map [arguments fields-to-update]
  (let [target_lpi (:target_lpi_as_date_str arguments)]
    (when target_lpi (swap! fields-to-update assoc :target_lpi (format "'%s'" (format-local-date (str-to-date target_lpi)))))
    @fields-to-update))

seancorfield20:10:02

First, a question about why you have the fields to update in an atom in the first place?

seancorfield20:10:50

If it's just to have it modified inside that function then: • just pass the hash map of fields • instead of when / swap!, do cond-> assoc

seancorfield20:10:53

So your function becomes:

(defn generate-update-input-map [arguments fields-to-update]
  (let [target_lpi (:target_lpi_as_date_str arguments)]
    (cond-> fields-to-update
      target_lpi
      (assoc :target_lpi (format "'%s'" (format-local-date (str-to-date target_lpi)))))))

seancorfield20:10:53

Then (generate-update-input-map args {:existing "fields" :to-be "updated"}) will return the fields with :target_lpi added if it needs to be there.

Abhi Saxena20:10:05

Got it, thanks Sean

seancorfield20:10:31

If you find cond-> hard to read, you could do

(if-let [target_lpi (:target_lpi_as_date_str arguments)]
  (assoc fields-to-update :target_lpi (format "'%s'" (format-local-date (str-to-date target_lpi))))
  fields-to-update))

Abhi Saxena20:10:46

this is what I was looking for.....

leif20:10:08

Is there any way I can make the pretty printer look less..umm...ugly for dictionaries? Right now, if I have a dictionary like:

{:k1 [:v1a :v1b ... :v1n]
 :k2 [:v2a :v2b ... :v2n]}
(Where the v1 and v2 lines go past the column limit, it will print it out as:
{:k1
 [:v1a
  :v1b
  :v1c
  ...
  :v1n],
 :k2
 [:v2a
  :v2b
  :v2c
  ...
  :v2n]}

leif20:10:51

But what seems like would be a lot cleaner to me would to print it out as:

{:k1 [:v1a
      :v1b
      :v1c
      ...
      :v1n],
 :k2 [:v2a
     :v2b
     :v2c
     ...
     :v2n]}

leif20:10:32

(For lists, vectors, and sets though, the current behavior makes sense. Its just dictionaries that seems...off...)

leif20:10:50

Currently my print code is:

(defn stx->stx-str [stx]
  (binding [cljs.pprint/*print-right-margin* 40]
    (with-out-str (pprint stx))))

leif20:10:56

(Using ClojureScript)