Fork me on GitHub
#beginners
<
2023-07-04
>
Elph07:07:40

πŸ‘‹ Hello, everyone

πŸ‘‹ 16
2
Jacob Doran07:07:23

I'm missing something, is there a "rest" concept when destructuring keys in a function argument?

Jacob Doran07:07:54

I'm trying to cascade props down into a react component. (defn button [{:keys [isLoading variant size class-name ref] :as props} children]...) But using the :as keyword gets me all the values, including the ones I've named, I'd like a :rest option to grab everything else? Any ideas or direction would be greatly appreciated.

delaguardo08:07:41

no, there is nothing like that in destructure syntax yet

daveliepmann08:07:11

A second binding using dissoc on the right hand side seems like it would do the trick, if I follow you correctly. It might help to describe your motivation some more.

phill11:07:01

Persistent data structures, higher-order functions, namespaced keywords, an interest in host-level performance, and clojure.spec suggest a philosophy of tolerating unexpected map keys. In other words, use what you need and ignore the rest. After all, the map with the excess is ready free-of-charge, while on the other hand making a new map takes time and, because of persistent structures, is usually pointless. Given that it's pretty easy to remove keys, when absolutely necessary, e.g., (reduce dissoc props [:boats :planes]), it's not a surprise that destructuring does not have a feature for the antipattern. @U04V4KLKC’s link had an interesting comment, which was that if this comes up often in your program and you don't like repeating the list of keys, you can make a macro.

πŸ‘ 6
Jacob Doran13:07:41

I did end up using dissoc, I normally would just leave the keys alone. As Phill says tolerating the unexpected keys. Unfortunately its interop with Javascript which isn't as sensible. I can definitely see why the core of clojure would not use this feature, I think it would be mostly helpful when using interop.

mandudeman12:07:49

I have these lines of code in a .clj file. When I try to run the file in a command-line, I am getting the error below: Execution error (ClassCastException) at user/eval1 (stat.clj:1). class java.lang.Character cannot be cast to class clojure.lang.Named (java.lang.Character is in module java.base of loader 'bootstrap'; clojure.lang.Named is in unnamed module of loader 'app') (require '[clojure.string :as str] '[org.clojure/math.numeric-tower "0.0.5" :as math]) ;;(def my_dataset (list 10 7 14 23 15 7 32 23)) ;;(def my_dataset (list 3 5 9 5 7 2)) (def my_dataset (list 67 66 71 33 63 72 74 58 78 75 67)) (defn comma-separated [coll] (str/join ", " coll)) (defn dataset-sum [coll] (reduce + coll)) (println (str "The elements in the dataset are the ff: " (comma-separated my_dataset) ".")) (println (str "The elements in the dataset are the ff: " (sort my_dataset) ".")) (println (str "The elements in the dataset in sorted order: " (comma-separated (sort my_dataset)) ".")) (println (str "The number of elements in the data set is " (count my_dataset) ".")) (println (str "The sum of all numbers in the dataset is " (dataset-sum my_dataset) ".")) (defn mode [coll] (let [counts (frequencies coll) ;; {7 2, 10 1, 14 1, 15 1, 23 1, 32 1} max-count (apply max (vals counts)) ;; 2 mode-entries (filter #(= max-count (val %)) counts)] ;; (2) (keys mode-entries))) ;; 7 (println (str "The mode of the dataset is " (mode my_dataset) ".")) (defn mean [coll] (if (empty? coll) nil (format "%.2f" (/ (float (reduce + coll)) (count coll))))) (println (str "The mean of the dataset is " (mean my_dataset) ".")) (defn median [coll] (let [sorted (sort coll) len (count sorted) mid (quot len 2)] (if (even? len) (/ (+ (nth sorted (dec mid)) (nth sorted mid)) 2) (nth sorted mid)))) (println (str "The median of the dataset is " (median my_dataset)".")) (defn collection-range [coll] (-(apply max coll) (apply min coll))) (println (str "The range of the dataset is " (collection-range my_dataset)".")) ;; define a function that calculates the standard deviation ;(defn standard-deviation [coll] ; (let [n (count coll) ; mean (mean coll) ; squared-deviations (map #(Math/pow (- % mean) 2) coll) ; variance (/ (reduce + squared-deviations) n)] ; (Math/sqrt variance))) (defn standard-deviation [coll] (let [numeric-coll (filter number? coll) ; Filter out non-numeric values n (count numeric-coll) mean (mean numeric-coll) squared-deviations (map #(math/expt (- % mean) 2) numeric-coll) variance (/ (reduce + squared-deviations) n)] (Math/sqrt variance))) (println (str "The standard deviation of the dataset is " (standard-deviation my_dataset) "."))

jpmonettas12:07:57

The require syntax is not right, looks like you are confusing dependencies with namespaces, you have :

(require '[org.clojure/math.numeric-tower "0.0.5" :as math])
with a artifact description and a version number there. Namespaces don't have versions, you should write the require like this :
(require '[clojure.math.numeric-tower :as math])
but after you are in a system that has org.clojure/math.numeric-tower {:mvn/version "0.0.5"}

jpmonettas13:07:05

also how are you trying to run this file?

jpmonettas13:07:01

You also have another typing issue, which is your mean fn is returning the mean as a string, so when you then try to do math with it you will get a class cast exception

jpmonettas13:07:05

so if you fix your require and also remove the format from your mean function you can run the entire script like this :

clj -Sdeps '{:deps {org.clojure/math.numeric-tower {:mvn/version "0.0.5"}}}' script.clj 

jpmonettas13:07:19

also if you are starting with Clojure developing your programs by running scripts like this is very time consuming. It is better to setup your editor to start a repl so you can evaluate and try each expression from it, and maybe just run it as a script when you know everything works. It takes some time to get used to it but really worth it

mandudeman05:07:48

I run the file in power shell of windows using wsl. In the command-line, I execute this command: clojure -M stat.clj

mandudeman05:07:41

After removing the version number, I still get this error: Execution error (FileNotFoundException) at user/eval1 (stat.clj:1). Could not locate math/numeric_tower__init.class, math/numeric_tower.clj or math/numeric_tower.cljc on classpath. Please check that namespaces with dashes use underscores in the Clojure file name.

jpmonettas11:07:45

it is not only about removing the version number, you also need to write it without the slash and with the correct namespace like I wrote before, copy this :

(require '[clojure.math.numeric-tower :as math])

jpmonettas11:07:26

also looking at your code you don't need that numeric-tower dependency at all, you can just (require '[clojure.math :as math]) and then just use math/sqrt and math/pow , then you can run your script much simpler like:

clj script.clj

mandudeman05:07:08

I did the following: 1. (require '[clojure.string :as str] '[clojure/math :as math]) I edit the code from this: '[clojure/math.numeric-tower :as math] To this: '[clojure/math :as math] 2. clj -Mdeps '{:deps {org.clojure/math.numeric-tower {:mvn/version "0.0.5"}}}' stat.clj This command downloaded the dependencies however, at the bottom, it threw an error like shown below: Execution error (FileNotFoundException) at http://java.io.FileInputStream/open0 (FileInputStream.java:-2). {:deps {org.clojure/math.numeric-tower {:mvn/version "0.0.5"}}} (No such file or directory) 3. clj -M stat.clj Execution error (FileNotFoundException) at user/eval1 (stat.clj:9). Could not locate math__init.class, math.clj or math.cljc on classpath. This threw an error as as it cannot find the '[clojure/math :as math].

jpmonettas10:07:08

the require is wrong, copy what I wrote, you are using slashes instead of dots, it should be (require '[clojure.math :as math]) Also you don't need the -M for executing the script, just do clj stat.clj

mandudeman11:07:56

That was an oversight on my part. But even after fixing it, I still get this error: Execution error (ClassCastException) at user/standard-deviation$fn (stat.clj:127). class java.lang.String cannot be cast to class java.lang.Number (java.lang.String and java.lang.Number are in module java.base of loader 'bootstrap')

jpmonettas11:07:20

yes, that is what I comment before > You also have another typing issue, which is your mean fn is returning the mean as a string, so when you then try to do math with it you will get a class cast exception

jpmonettas11:07:54

it will work if you remove the (format ...) you have in your mean fn

mandudeman11:07:52

Yes, it did. If I do not use the format function, how will I format the results into two decimal places?

jpmonettas11:07:39

you should format them just for printing, so you can do it on your printlns

jpmonettas11:07:26

like :

(println (format "The mean of the dataset is %0.2f " (mean my_dataset)))

mandudeman11:07:58

Ok. Thanks a lot

mandudeman12:07:33

I only encounter this error after I added these lines below:

mandudeman12:07:36

(defn standard-deviation [coll] (let [numeric-coll (filter number? coll) ; Filter out non-numeric values n (count numeric-coll) mean (mean numeric-coll) squared-deviations (map #(math/expt (- % mean) 2) numeric-coll) variance (/ (reduce + squared-deviations) n)] (Math/sqrt variance))) (println (str "The standard deviation of the dataset is " (standard-deviation my_dataset) "."))

James Amberger14:07:34

Is there a way to persist REPL state automatically, such that one could recover from, e.g., a power failure or an accidental termination (of the repl or the parent process or whatever)?

delaguardo14:07:47

imho, the best way to persist REPL state is to write AND execute code directly from the editor. There are clojure plugins that allow that for almost every popular editors and IDEs

delaguardo14:07:54

for example you can create a comment block right in your project's files:

(comment
  
  (def tmp-value 42)

  (run-experiment tmp-value)

  )

solf14:07:47

(I was thinking the question was about fully saving a JVM state, like jvm snapshot), but @U04V4KLKC interpretation makes more sense :))

Ben Sless14:07:37

you can also implement your own eval which persists vars to a kv database and load it when you start πŸ™ƒ

Ben Sless14:07:33

slightly more complicated with defn s because you'll have to either find a way to persist closures or do something else

delaguardo14:07:08

@UK0810AQ2 (def fuuuuu (range)) πŸ™‚

delaguardo14:07:42

even vars can give you a headache πŸ™‚

Ben Sless14:07:28

@U04V4KLKC nippy can handle ranges, no?

delaguardo14:07:38

I don't think so

delaguardo14:07:41

but I rarely use it so maybe I'm missing something like custom serde

Ben Sless14:07:14

Another option is event-sourcing the REPL state when it can't be saved

James Amberger16:07:43

Can you expand on that @UK0810AQ2?

Ben Sless16:07:17

The dumbest way to do it is just append to a file every form you eval, then loading that file should recreate your REPL state exactly. Everything afterwards is optimization

Ben Sless16:07:53

sort of like another event sourced functional database πŸ™ƒ

Ben Sless16:07:22

Hell, if you can serialize everything (you can't but we're theorizing), replace the implementation of namespaces to use a database and save all values there

James Amberger16:07:47

yes, yes, I am looking at my question differently now. If I have data to persist there are plenty of options and if I have code to persist I have an editor.

πŸ’‘ 1
Ben Sless16:07:37

But if you have entire REPL state to persist, there's still no optimal solution for your problem, although #C035GRLJEP8 does interesting things in that direction

James Amberger16:07:14

#C035GRLJEP8 is notebooking iirc

James Amberger16:07:31

from the guy who doesn’t like notebooks. very cool.

jpmonettas18:07:14

I guess the complicated part around the event sourcing repl or even just having a big comment with a lot of forms are side effects, since you are re running them for the side effects to recover you repl state, but also mixed will be side effects that aren't related to the JVM process state but with something like your DB state, that you don't need to rebuild because it never got destroyed

Ben Sless18:07:46

That's where you start optimizing and replace the results of side effects with values

Ben Sless18:07:16

you can do that pretty much indefinitely as long as you aren't dealing with infinite stuff or things which aren't values

jpmonettas18:07:43

I don't think that is possible to automate, you may had executed a fn-call that incremented an atom and created a file on the disk, so for recovering your repl state you are interested in the first side effect but no the second one