Fork me on GitHub
#beginners
<
2021-11-28
>
Dmitrii Pisarenko13:11:23

Hello! Imagine I have the state atom defined as:

(defonce state
         (atom {
                :txs []
                }))
In the function btcTxReceived I want to 1. take the old value of state, 2. append new-tx to the :txs list, 3. set state to the updated map.
(defn append-tx
  [old-state new-tx]
  (let [
        old-tx-list (get old-state :txs)
        new-tx-list (conj old-tx-list new-tx)
        ]
    (assoc old-state :txs new-tx-list)
    ))

(defn btcTxReceived
  [wallet
   tx
   prev-balance
   new-balance]
  (let [
        new-tx (create-btc-tx
                 wallet
                 tx
                 prev-balance
                 new-balance)
        ]
    (swap! state append-tx new-tx)
    )
  )
Is this the right way to do it?

emccue13:11:14

so mechanically yes that will work

emccue13:11:37

but some general feedback

emccue13:11:58

(defn append-tx
  [old-state new-tx]
  (let [old-tx-list (get old-state :txs)
        new-tx-list (conj old-tx-list new-tx)]
    (assoc old-state :txs new-tx-list)))


(defn btcTxReceived
  [wallet
   tx
   prev-balance
   new-balance]
  (let [new-tx (create-btc-tx
                wallet
                tx
                prev-balance
                new-balance)]
    (swap! state append-tx new-tx)))

emccue13:11:53

I know it probably feels crazy at this point, especially coming from nearly any other language, but trust me, don’t give parens their own lines

emccue14:11:04

next, if i expand out what i imagine your code to be i get something like this

emccue14:11:36

(def state (atom {:txs []}))

(defn create-btc-tx
  [wallet tx prev-balance new-balance]
  (... burn rainforest ...))

(defn append-tx
  [old-state new-tx]
  (let [old-tx-list (get old-state :txs)
        new-tx-list (conj old-tx-list new-tx)]
    (assoc old-state :txs new-tx-list)))


(defn btcTxReceived
  [wallet
   tx
   prev-balance
   new-balance]
  (let [new-tx (create-btc-tx
                wallet
                tx
                prev-balance
                new-balance)]
    (swap! state append-tx new-tx)))

emccue14:11:43

assuming create-btc-tx actually does a side effect upon the world, putting a ! at the end is one way to signal that visually

emccue14:11:33

(although i will be the first to admit i am super inconsistent about that in my own code)

emccue14:11:55

(def state (atom {:txs []}))

(defn create-btc-tx!
  [wallet tx prev-balance new-balance]
  (... burn rainforest ...))

(defn append-tx
  [old-state new-tx]
  (let [old-tx-list (get old-state :txs)
        new-tx-list (conj old-tx-list new-tx)]
    (assoc old-state :txs new-tx-list)))


(defn btc-tx-received!
  [wallet
   tx
   prev-balance
   new-balance]
  (let [new-tx (create-btc-tx!
                wallet
                tx
                prev-balance
                new-balance)]
    (swap! state append-tx new-tx)))

emccue14:11:28

and then depending on the size of your program this may or may not matter

emccue14:11:19

but generally you should try to take state as an argument instead of having one global which you refer to

emccue14:11:38

(defn create-state-atom 
  []
  (atom {:txs []}))

(defn create-btc-tx!
  [wallet tx prev-balance new-balance]
  (... burn rainforest ...))

(defn append-tx
  [state new-tx]
  (let [old-tx-list (get old-state :txs)
        new-tx-list (conj old-tx-list new-tx)]
    (assoc old-state :txs new-tx-list)))


(defn btc-tx-received!
  [state-atom
   wallet
   tx
   prev-balance
   new-balance]
  (let [new-tx (create-btc-tx!
                wallet
                tx
                prev-balance
                new-balance)]
    (swap! state append-tx new-tx)))

(defn -main 
  []
  (let [state-atom (create-state-atom)]
    (btc-tx-received! state-atom ...)))

emccue14:11:08

at which point btc-tx-received takes 5 arguments, which is usually my tipping point for using keyword arguments

emccue14:11:46

but thats probably enough for now

Jakob Durstberger16:11:35

Is there a function that returns a function that inverts the result of a predicate? Aka (xxx (empty?)) instead of having to write #(not (empty? %))

emccue16:11:12

well for empty? in particular there is not-empty

Mno16:11:42

I think there was one called complement?

emccue16:11:47

yeah that^

emccue16:11:11

(complement empty?) will give the opposite boolean result as empty?

Jakob Durstberger16:11:52

Yeah complement! Thanks so much ... I thought I was losing my mind 😄

pithyless17:11:47

@UM8P1G72Q just FYI - if (complement empty) is specifically what you're looking for, and not just an example, then you should use (seq ,,,) instead of (not (empty? ,,,)) - empty? is already implemented as (not (seq ,,,)) in Clojure core.

Jakob Durstberger17:11:45

interesting, didn't know that

pithyless17:11:29

fun fact: the docstring for empty? includes this: > Please use the idiom (seq x) rather than (not (empty? x)) https://clojuredocs.org/clojure.core/empty_q

seralbdev16:11:28

Hi all, beginner here (I am afraid I will be always beginner in Clojure :D) ... I have a Clojure program running using a library that creates lots of log. I am using clojure.tools.logging with logback backend. The current logback config sets the appender to console. I can see the log in the console in which I start the program but I have all that log at the same time in the EMACS REPL (nrepl connection). Is there anyway to dissoc the console output appearing at the REPL ??? I would like to have the log output ONLY in the console. I have been scanning posts in the internet ... but no luck ...Thanks a lot in advance!!

popeye17:11:46

I am trying https://github.com/ptaoussanis/carmine , when I see that there is no set found in the namespace taoensso.carmine , how to refer set ? I see that there is ser method present https://ptaoussanis.github.io/carmine/taoensso.carmine.html#var-set, What am I missing? cc @ptaoussanis could you please help me to understad this

pithyless17:11:24

I'm not familiar with carmine, but it looks like it's happening here: https://github.com/ptaoussanis/carmine/blob/master/src/taoensso/carmine.clj#L205

(commands/defcommands) ; Defines 190+ Redis command fns as per official spec
Which looks like this here: https://github.com/ptaoussanis/carmine/blob/master/src/taoensso/carmine/commands.edn

popeye18:11:26

if it is not present in namespace, will that strill loads ?

pithyless18:11:52

yes, because when you load toaensso.carmine namespace, it will eval (commands/defcommands) and that in turn is a macro that will dynamically create all those redis commands as new vars in the namespace. See: https://github.com/ptaoussanis/carmine/blob/master/src/taoensso/carmine/commands.clj#L240-L256

🙌 1
popeye18:11:26

good catch ! Thank you 🙂

Jakub Zika18:11:33

Hi all, I just read a Clojure book - Clojure for finance - where the author start with a fn like this:

(reduce 
 (fn [rslt x] 
  (let [y (if (last rslt) (last rslt) 0)]
    (lazy-cat rslt [(* y x)])))
    '() 
     lst)
then - by the end of the book - he updates it to (by his words) to better and more lazy version
(last (reductions
 (fn [rslt x] 
  (let [y (if (last rslt) (last rslt) 0)]
    (lazy-cat rslt [(* 2 x)])))
    '() 
     lst))
Would you do this? I don’t find the second version really intuitive… (+ not sure about lazy-cat)

Jakub Zika18:11:37

btw. Clojure for finance is a really horrible book - a lot of errors, broken styling etc

delaguardo19:11:28

Looks odd to me. First argument for reduce and reductions should be a function. Technically vector is a function suitable for reduce but i don't think it is intuitive to read.

Jakub Zika19:11:20

Aah! Wrong copy paste. That should be of course…

(reduce (fn [rslt x] 
  (let [y (if (last rslt) (last rslt) 0)]
    (lazy-cat rslt [(* y x)])))
    '() 
     lst)
… My question is more about reduce vs -> reductions last

phronmophobic20:11:01

It's hard to imagine why last+reductions might be better than reduce. seems categorically worse to me. The only reason you might try that is to lazily produce the result, but there are other better ways to do that.

👍 1
pithyless21:11:18

I'm really confused about this example; reductions seems like a step backwards in readability here; calling (last rslt) is O(n) operation on a list; and as far as I can figure out.... this will always multiply all numbers by 0, so in essence this looks equivalent to (repeat (count lst) 0)

seancorfield21:11:05

It's a Packt book?

seancorfield21:11:09

Yeah... They're nearly all terrible. Packt approached me to write a book but their process was awful and I have several friends who've written for them and mostly said "never again".

Jakub Zika06:11:26

@U05476190 There is different calculation in the book - i just quickly mockuped the inner function. I could do better 🙂. @U04V70XH6 Yeah, really terrible. I understand why they took it from their market.

Jakub Zika06:11:52

@U7RJTCH6J Yup. After introducing the last reductions author states. Now that our analytic functions are lazy, we can start thinking about what signals we may be interested in.

phronmophobic06:11:21

unless I'm looking at it wrong, last makes it not lazy :thinking_face:

roelof21:11:57

Why is the math not known here

(ns armstrong-numbers
  (require (clojure.math.numeric-tower :as math :refer [expt])))

(defn armstrong? [num] ;; <- arglist goes here
  (let [numbers (map #(Character/digit % 10) num)
       count_numbers (count numbers) ]
  (-> count_numbers
       (math/expt numbers ))))

andy.fingerhut21:11:45

With Clojure version 1.10.1, that ns forms gives me error messages as you wrote it. It gives no errors if you use this one intead:

andy.fingerhut21:11:53

(ns armstrong-numbers
  (:require [clojure.math.numeric-tower :as math :refer [expt]]))

roelof21:11:10

thanks, the example I found was wrong

roelof21:11:45

another problem I have this

(ns armstrong-numbers
  (:require [clojure.math.numeric-tower :as math :refer [expt]]))

(defn armstrong? [num] ;; <- arglist goes here
  (let [numbers (map #(Character/digit % 10) num)
       count_numbers (count numbers) ]
  (->>  numbers
       (map (expt count_numbers )))))

roelof21:11:11

but see this error message :

actual: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long

roelof21:11:49

What I try to do it that all the numbers in the numbers collection the exponation is to the power of the numbers of the given number

andy.fingerhut22:11:58

Do you get that error message from entering the code you have shown? Or from trying to call the function armstrong? with some particular argument? If calling armstrong? , what call are you trying to make that gives that error?

Chase23:11:39

in your let binding you are trying to use your number parameter as a sequence in the map function but that doesn't work.

Chase23:11:08

that mapping function expects a seq of chars so you would use (str num) as that will get coerced(?) into what you need

roelof05:11:13

hmm, I think I misunderstand you

roelof05:11:41

Still this is not working

(defn armstrong? [num] ;; <- arglist goes here
  (let [numbers (map #(Character/digit % 10) num)
       count_numbers (count numbers) ]
  (->>  (str numbers)
       (map #(expt count_numbers % )))))
still the same annoying error message

roelof06:11:55

but this is working

(defn armstrong? [num] ;; <- arglist goes here
  (let [numbers (map #(Character/digit % 10) (str num))
       count_numbers (count numbers) ]
  (->>  numbers
       (map #(expt % count_numbers ))
        (reduce +)
        (== num))))

andy.fingerhut07:11:38

The function call (map any-fn-here num) expects num to be a sequence, or a type of value that Clojure can turn into a sequence, e.g. a vector, list, or string (which is turned into a sequence of characters).

andy.fingerhut07:11:49

For example:

user=> (map #(Character/digit % 10) "12345")
(1 2 3 4 5)
user=> (map #(Character/digit % 10) 12345)
Error printing return value (IllegalArgumentException) at clojure.lang.RT/seqFrom (RT.java:557).
Don't know how to create ISeq from: java.lang.Long

andy.fingerhut07:11:38

You still have not said what kinds of parameter values you are expecting to pass to your function armstrong? strings that contain only decimal digits? an integer? something else?

roelof07:11:01

a integer. See this test

(deftest armstrong-number-153
  (testing "Three digit number that is an Armstrong number"
    (is (armstrong? 153))))

andy.fingerhut15:11:12

Does it make sense that the call (map any-fn-here 153) throws an exception, because the number 153 is not a Clojure sequence?

roelof15:11:38

I was lookimg at the wrong place when I saw that error-message

brendnz22:11:21

If you run a form from a repl, does clojure code compile a jvm class file in memory and run it? If you then edit that form and re-run it, does clojure then create a new jvm class in new memory? What happens to the memory occupied by the previous class of the same name? Is it garbage collected? Does clojure need java to compile its jvm class files or does it compile them independent of java? Is the object code a jvm class or a java class file? Equivalently I think, is the structure of class files independent of the java programming language? If I paraphrase this with respect to say the CLR: Does clojure need c# to compile its CLR class files or does it compile them independent of c#? Are they CLR class files or c# class files? Equivalently I think, is the structure of CLR class files (assuming CLR has class files) independent of the c# programming language? Please excuse the extent to which I do not know what I am talking about.

andy.fingerhut22:11:18

Yes, when using Clojure on the JVM (i.e. not ClojureScript), the Clojure compiler always compiles the Clojure source code to JVM byte code before executing it.

andy.fingerhut22:11:02

The Clojure compiler does not use any Java compiler while compiling. It compiles Clojure code to JVM byte code in memory, without needing to generate any Java source code at any point of the process.

andy.fingerhut22:11:11

The Clojure compiler also does not typically write any files when creating JVM byte code. It typically creates a class and its byte code in memory, without having to write a JVM class file. The Clojure compiler can write Java .class files, if you wish.

andy.fingerhut22:11:38

"If you then edit that form and re-run it, does clojure then create a new jvm class in new memory?" Yes, it does.

👀 1
andy.fingerhut22:11:56

"What happens to the memory occupied by the previous class of the same name? Is it garbage collected?" I do not know if it is garbage collected or not.

brendnz22:11:29

Thank you Andy, very much appreciated.

Alex Miller (Clojure team)23:11:20

It is gc’ed in modern jvms