Fork me on GitHub
#beginners
<
2020-11-04
>
Marcus10:11:41

Is it possible to reload dependencies while in the repl? I have some local libraries installed in the .m2 directory and currently I have to restart the repl to use the latest version.

Daniel Stephens11:11:05

haven't used it yet personally but I've heard some nice things about https://github.com/clj-commons/pomegranate to do this

Daniel Stephens11:11:35

also for files that you have the source of, if you use cursive you can often just go to a file in the library, make your changes, and then evaluate the file.

Marcus12:11:19

Thanks @ULNRSUK8C! I will check it out. :)

👍 3
Marcus12:11:17

I have not tested cursive yet but it looks interesting

noisesmith15:11:37

I've used pomegranate to add deps, I didn't realize it could replace them

Louis Kottmann12:11:11

I've used pomegranate with success

Christian13:11:23

Hi there, I'm doing the 4clojure problems to understand fp and clojure. I'm trying to count a sequence without using count. So my idea was to get the last element of the seq with last and then getting the last index of that element with last-element-of. so for '( 1 2 3 1) last should return 1 and the index should be 3. Count would be 3+1. But I can't get last-index-of to work: (fn newcount [x] (+ 1 (.last-index-of x (last x))))

dominem13:11:29

Isn't reduce the answer in this case?

Christian13:11:37

maybe, I don't know the reduce function. have to look that up. do you have an idea, why last-index-of is not working?

dominem13:11:52

Hint: Ask yourself what does (last seq) return. Hmm.. sorry, I just misinterpreted your code. I'll check in a moment :)

dominem13:11:10

What is .last-index-of (note the dot - .)?

dominem13:11:41

But there is .lastIndexOf:

user=> (def x '(1 2 3 1))
#'user/x
user=> (last x)
1
user=> (.last-index-of x (last x))
Execution error (IllegalArgumentException) at user/eval2007 (REPL:1).
No matching method last_index_of found taking 1 args for class clojure.lang.PersistentList

user=> (.lastIndexOf x (last x))
3
user=>

dominem13:11:45

But I don't think this is the way you should solve this issue.

tugh13:11:05

@U01E6385CN8 there are 2 problems with your code. 1. as mentioned in the docs last-index-of only works on strings and you're not passing a string 2. dot notation is for interop(https://clojure.org/reference/java_interop#_the_dot_special_form) you can just (last-index-of [1 2 3] 1) notice that there is no dot in the beginning

Christian13:11:27

what is interop?

dominem13:11:18

It's the mean you can *interop*erate with Java thanks to.

tugh13:11:42

it is a mechanism to leverage libraries from the host environment. in clojure the host is jvm in clojurescript the host is js

practicalli-johnny14:11:21

One approach to this is to create a new sequence with a numeric value for each element in the original sequence, then sum the values in the new sequence.

Christian14:11:40

I managed to solve it with reduce, pretty interesting way of doing things. @UK8ELHM5F thanks for explaining

✌️ 3
practicalli-johnny14:11:19

I think my favourite approach was map and reduce, using constantly to create the new seq. I did it lots of ways here... https://github.com/practicalli/four-clojure/blob/master/src/four_clojure/022_count_a_sequence.clj

mloughlin13:11:30

Maybe I don't understand the question, but why would the last element's value be needed to count the seq?

Christian13:11:58

because that is my idea 😄 I'm not allowed to just use the count function. so that is what came to my mind as an alternative

Thomas13:11:58

You could try a recursive approach, the length of an empty sequence is 0, otherwise it’s 1 plus the length of the rest

mloughlin13:11:21

If you're still getting the hang of functional programming, I would say: How would you do this in an imperative language?

mloughlin13:11:46

Probably a for loop of some kind and an int that we increment in this case

mloughlin13:11:40

in Clojure, the first things I think of when working with all of the items in a sequence is "Can this be done with map , filter or reduce? (in that order)"

👀 3
mloughlin13:11:03

then if none of those fit the problem, a loop/`recur` will almost certainly do the trick

dominem13:11:19

:thumbsup: I like this way of thinking.

Christian13:11:02

interesting.... thank's for the pointers

Christian14:11:09

okay, reduce did the trick. thanks everyone

3
dominem14:11:25

you're welcome 🙂

mloughlin14:11:16

Can anyone think of a more concise way of writing this fn? It returns a property or nil depending on the value of another property in the same map

(defn nessus-id [entry]
  (let [plugin-id (get-in entry [:fields :plugin_id])]
    (when (and (= "nessus" (get-in entry [:fields :plugin]))
               plugin-id)
      plugin-id)))

mloughlin14:11:41

I guess the and is unnecessary

mloughlin14:11:19

and it follows that the let is also not needed 🙂

mloughlin14:11:30

(defn nessus-id [entry]
  (when (= "nessus" (get-in entry [:fields :plugin]))
    (get-in entry [:fields :plugin_id])))

dominem14:11:05

Just a bit simpler:

(defn nessus-id [entry]
  (let [pid (get-in entry [:fields :plugin_id])]
    (when (= pid "nessus") pid)))

mloughlin14:11:42

my mistake, the "nessus" string is from :plugin not :plugin_id

dominem14:11:19

ahh, then it won't work my way

noisesmith16:11:50

(defn nessus-id [{{:keys [plugin plugin_id]} :fields}] 
  (when (= plugin "nessus")  
    plugin_id))

jaihindhreddy16:11:52

Perhaps https://clojure.org/guides/destructuring can help:

(defn nessus-id [{{:keys [plugin plugin_id]} :fields}]
  (when (and plugin_id (= plugin "nessus"))
    plugin_id))

jaihindhreddy16:11:48

The two solutions above differ when plugin_id is false (and plugin is equal to "nessus"). Mine will return nil whereas noisesmith's will return false. Just something to be aware of.

mloughlin16:11:02

nested destructuring doesnt have the most intuitive syntax

mloughlin16:11:36

but besides that, I like it!

mloughlin16:11:02

Time to re-read the destructuring docs

noisesmith16:11:58

all that's used here: {foo :bar} - sets foo to the value under :bar in the map, {:keys [a b]} sets a,b to the values of :a :b in the map

noisesmith16:11:07

bindings are on the left because map keys must be unique, so it restricts reuse of bindings instead of keys so you can use the same key multiple places

mloughlin16:11:56

I see {:keys [a b]} all the time, not so often {foo :bar}

Jim Newton14:11:16

I don't understand :post conditions for function definitions. I define a function as follows

(defn spec-to-rte 
  [pattern]
  {:post [(fn [v] (println [:spec-to-rte :returns v]) true)]}
 ...)
I can call the function and see its return value in the repl, but the println in the :post condition doesn't seem to be called, or at least its stdout is suppressed. Am I doing something obviously wrong?

andy.fingerhut14:11:39

You gave it an expression to evaluate, and it will evaluate it, returning a function, which is always logical true.

andy.fingerhut14:11:45

so not a useful postcondition.

Jim Newton14:11:12

isn't the post condition called with the return value of the function?

Jim Newton14:11:12

Here's the example in the documentation. https://clojure.org/reference/special_forms

andy.fingerhut14:11:37

Those are expressions that are evaluated, returning true or false.

andy.fingerhut14:11:43

Your expression, when evaluated, returns a function.

Jim Newton14:11:57

the post condition doesn't return (> % 16) but rather calls it with the return value of the funciton. right?

andy.fingerhut14:11:06

every object/value in Clojure that is neither nil nor false is logical true.

Jim Newton14:11:38

so in the documentation is (> % 16) always true?

andy.fingerhut14:11:38

% is replaced with the return value, at least in :post expressions, yes.

Darin Douglass14:11:51

:pre/`:post` expand out into asserts that run before/after your body. the source is pretty enlightening with regards to that https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L4562

andy.fingerhut14:11:18

When you evaluate (> % 16) after replacing % with the return value, if that return value is such that evaluating (> % 16) does not throw an exception, then it will return true or false.

Jim Newton14:11:34

ahh I thought (> % 16) was an anonymous function

Jim Newton14:11:50

doesnt that have a very similar syntax?

andy.fingerhut14:11:55

It looks like one, but #(> % 16) is an anonymous function.

Jim Newton15:11:38

ouch, that's really confusing. Am I the only one that was confused by that?

Darin Douglass15:11:29

nope, i've personally never liked pre/post.

andy.fingerhut15:11:45

The Eastwood linter can catch many such silently-always-true :pre and :post conditions. It hasn't been updated in a while, but still works for a lot of Clojure/JVM code bases, I think (it does not support ClojureScript)

Jim Newton15:11:03

wow looking at the source code is horrible. that means % is a hacky reserved variable in the post condition. If my code contains something like #(f % 12) it will do wierd things.

andy.fingerhut15:11:18

I suspect that the reader might handle % inside #( ... ) expressions before the :pre and :post handling code ever get to look at it, but not sure about that. It would be clearer not to include anonymous functions in :pre and :post expressions, for sure.

Jim Newton15:11:54

why wan't this done in a functional way? if :post specified a sequence of functions which should be called and all return true. I could write something like {:post [even? pos?]} and with would be more consistent with the language? right?

Jim Newton15:11:04

OK, it is what it is.

andy.fingerhut15:11:28

I did not design it, nor have I asked the designer why they chose that way.

Jim Newton15:11:23

OK, I changed it to

(defn spec-to-rte 
  [pattern]
  {:post [(do (println [:spec-to-rte :returns %]) %)]}
...)
and it works correctly. hurts my eyes to look at though

andy.fingerhut15:11:27

I am not trying to imply that such questions are useless, but some questions like that have well known design decisions that many in the Clojure community know, but some are much less well known, perhaps only to one person.

Jim Newton15:11:46

yes you're right.

Jim Newton15:11:31

important thing is that I understand now.

andy.fingerhut15:11:48

Unit testing of such things is a good practice, e.g. in the REPL, especially the first time you use a new language construct. If you can't make a :post condition fail, even though you believe your condition should obviously be false, that is a sign that you are not using the correct expression there.

andy.fingerhut15:11:47

In your last version of the :post condition above, is your goal that there should be an assertion exception thrown if spec-to-rte returns nil or false? Because I believe that is what that code will do.

andy.fingerhut15:11:13

If you want run-time trace messages, there is a library called tools.trace that might also suit your needs: https://github.com/clojure/tools.trace

Jim Newton15:11:28

ouch yes you're right. no, my intention is just to trace the result and don't assert anything.

Jim Newton15:11:48

is there a better way to trace the input and output of a function than by using :pre and :post ?

andy.fingerhut15:11:39

I tried to mention one way above .... (see link to tools.trace library in my previous message -- not saying it will suit your needs, but that is exactly what that library is intended to help with)

Jim Newton15:11:27

ahhh, thanks for the link. I hadn't read the end of your message because I was busy fixing the bug that the first half of your message pointed out.

Darin Douglass15:11:34

another options would be a logging library like mulog (https://github.com/BrunoBonacci/mulog)

Jim Newton15:11:36

BTW, to use that trace library, I need to add something to my .lein/profiles.clj file. Is there a way to load the dependency [org.clojure/tools.trace "0.7.10"] without restarting?

Darin Douglass15:11:44

mulog.core/trace has a way to capture output of fns into logs

Jim Newton15:11:42

capturing into log files might be interesting, because sometimes my code redirects stdout by (binding [**out** ...] ...) and then tracing to stdout confuses everything.

andy.fingerhut15:11:20

I have heard people using some techniques to add libraries to your Java classpath without starting a new JVM, but I haven't used them or have any handy links explaining how. I tend to start a new JVM process when I add new libraries.

andy.fingerhut15:11:33

It should work to do git clone of the Clojure source code, and then do one or more load-file calls from a REPL session, with the full path to the source code, but you would have to do them in the order from "bottom up" so that require calls never needed to look for a source file in your class path. (this is a very tedious way to do it if there are many namespaces, and not recommended -- I only mention it because it is technically possible to do).

dharrigan15:11:35

How might this be modelled in clojure?

dharrigan15:11:37

(input.get() & 0xFF)
                | ((input.get() & 0xFF) << 8)
                | ((input.get() & 0xFF) << 16)
                | ((input.get() & 0xFF) << 24)

andy.fingerhut15:11:22

There are bit-and bit-or and bit-shift-left functions in Clojure that work pretty much like & | and <<

dharrigan15:11:36

Yup, found those, it's just the application that would stump me

andy.fingerhut15:11:39

user=> (defn input-get []
  ;;; code here to emulate input.get()
  0x17
  )

#'user/input-get
user=> (def next-4-bytes-as-32bit-word
  (bit-or (bit-and (input-get) 0xff)
          (bit-shift-left (bit-and (input-get) 0xff) 8)
          (bit-shift-left (bit-and (input-get) 0xff) 16)
          (bit-shift-left (bit-and (input-get) 0xff) 24)))

#'user/next-4-bytes-as-32bit-word
user=> (format "%08x" next-4-bytes-as-32bit-word)

"17171717"

ghadi15:11:22

you can also use a ByteBuffer's getInt method to swap the endianness

💯 3
noisesmith18:11:13

agreed, ByteBuffer is more elegant for taking N bytes and deriving any primitive numeric type (not to mention the only sane way to do eg. a float)

dharrigan19:11:38

that's great - just looked at the java api. wonderful!

ghadi15:11:48

that's a standard pattern of reading 4 bytes and reconstructing a 32 byte integer

dharrigan15:11:28

thank you both. Andy - studying... 🙂

Anders Nygren16:11:41

Hi all! I need help with video tip. We are five javascript developers that has set out to do a small project in clojurescript + node + express during a couple of meetups. Four of us have not written any clojure, and the fith not much, so our first meetup (4h) is planned to be an intro to clojure/clojurescript. It would be splendid if we could find some video with a primer on syntax, but primarily on the common data-collections and how to manipulate them. Not too long as everybody is eager to hop into the editor 🙂 Any tip would be gratefully received!

andy.fingerhut17:11:47

For videos, I have heard good reviews about Eric Normand's https://purelyfunctional.tv/ but I have not viewed them myself. He has been offering reduced rates during Covid lockdown time to those who are not able to pay, if you contact him personally. Even if you are able to pay but would like a free sample before deciding to purchase, I imagine he might consider such requests.

dvingo18:11:21

There are some more here too: https://www.jacekschae.com/

Anders Nygren20:11:04

Thanks! Will check it out 🙂

noisesmith18:11:13

agreed, ByteBuffer is more elegant for taking N bytes and deriving any primitive numeric type (not to mention the only sane way to do eg. a float)

Roger Amorin Vieira19:11:14

Hi I have this string in a file: DÉBITO In the clojure after reading the file: DEB\ufffdTO Someone know how can I remove this special character?

dpsutton19:11:11

how are you reading the file?

Roger Amorin Vieira19:11:47

(defn read-file
   [path]
   (with-open [rdr ( path)]
      (reduce conj [] (line-seq rdr))))

seancorfield19:11:30

Are you sure it's not just the Unicode representation being shown when you print the value?

dpsutton19:11:34

/t/stuff ❯❯❯ echo "DÉBITO" > chars
/t/stuff ❯❯❯ clj
Clojure 1.10.1
user=> (slurp "chars")
"DÉBITO\n"
user=> (with-open [rdr ( "chars")] (into [] (line-seq rdr)))
["DÉBITO"]
user=>

dpsutton19:11:28

is there a default encoding for jvms?

walterl19:11:33

What encoding was the file saved in?

ghadi19:11:54

the original symptom is not clear you're reading a string DEBITO from DEPOSITO?

ghadi19:11:06

need to re-evaluate your facts

ghadi19:11:22

P's don't turn into B's when you read them

ghadi19:11:46

and I really hope that a debit doesn't turn into a deposit, and if it does, let me sign up for your bank please

Roger Amorin Vieira19:11:47

Sorry I wrote wrong, is DEPOSITO and DEBITO the problem

ghadi19:11:17

ok so what's the content of the file?

Roger Amorin Vieira19:11:36

I have a function that remove special characters

(defn deaccent [str]
   "Remove accent from string"
   ;; 
   (let [normalized (java.text.Normalizer/normalize str java.text.Normalizer$Form/NFD)]
      (clojure.string/replace normalized #"\p{InCombiningDiacriticalMarks}+" "")))

ghadi19:11:39

unicode fffd is a "unknown replacement character" meaning some sort of sadness already happened

👍 3
Roger Amorin Vieira19:11:05

But in tests remove 100% the Ó but in the run dont, i thing is becaus of \

Roger Amorin Vieira19:11:31

In my database I have this error when I try to submit the file Incorrect string value: '\xEF\xBF\xBDdit...' for column 'name' at row 1

walterl19:11:18

\ufffd is often seen when trying to decode a Unicode string with an encoding other than the one the string was encoded in.

walterl19:11:52

E.g. your file content is possibly encoded in latin-1, and you're trying to decode as utf-8

Roger Amorin Vieira19:11:20

If I do (str/replace string #"\ufffd" "") might work?

dpsutton19:11:48

i think you're papering over your problem. if you can identify why \ufffd characters (codepoints?) are ending up in your string you'll likely properly fix the underlying problem

ghadi19:11:57

check (System/getProperties "file.encoding") on both your local env, and your CI, and your server

ghadi20:11:20

hopefully it's UTF-8 everywhere

ghadi20:11:31

then whatever your database is needs to not mangle UTF-8

Roger Amorin Vieira20:11:02

Yes, I will need to fix the origin of this issue

Roger Amorin Vieira20:11:13

Thanks for all help, I will try what you told @ghadi

walterl20:11:47

@contato509

; eval (current-form): (spit "foo.txt" "DÉBITO" :encoding "ISO_8859_1")
nil
; --------------------------------------------------------------------------------
; eval (current-form): (slurp "foo.txt")
"D�BITO"
; --------------------------------------------------------------------------------
; eval (current-form): (slurp "foo.txt" :encoding "ISO_8859_1")
"DÉBITO"

walterl20:11:24

Related: TIL how to set encoding for writing/reading files neckbeard

mbjarland21:11:14

quick question, I’m playing with clojure koans and have the following code:

(ns binary-search)

(defn middle [coll]
  (quot (count coll) 2))

(defn search-for
  [x coll]
  (when (empty? coll) (throw (RuntimeException. "not found")))
  (let [i (middle coll)
        c (nth coll i)]
    (cond
      (= c x) i
      (> c x) (search-for x (take i coll))
      (< c x) (+ i 1 (search-for x (drop (inc i) coll))))))
which does binary search in a coll. What I don’t understand is, why does the following not blow the stack?
(search-for 9999999 (range 10000000))
=> 9999999
As I am not using recur, I figured this should be toast. JVM doing TCO even without recur? Also printed the stack at around 100 000 and it is very shallow. [the rest of the code is really irrelevant, I’m trying to understand why a self recursive function doesn’t blow the stack after 100k recursive calls].

phronmophobic21:11:41

if it's dividing the search coll in half every iteration, it should only do ceil(log2(10000000)) = 24 recursive calls max, right?

hiredman22:11:07

range also has some tricks up its sleave

hiredman22:11:40

user=> (type (range 1000))
clojure.lang.LongRange
user=> (supers clojure.lang.LongRange)
#{clojure.lang.IPersistentCollection java.io.Serializable clojure.lang.ASeq clojure.lang.IChunkedSeq clojure.lang.Counted clojure.lang.IMeta clojure.lang.Obj clojure.lang.IHashEq clojure.lang.Seqable clojure.lang.ISeq java.lang.Iterable clojure.lang.IReduceInit java.util.Collection clojure.lang.IObj java.util.List java.lang.Object clojure.lang.Sequential clojure.lang.IReduce}
user=>

mbjarland06:11:35

@U7RJTCH6J doh, yes. Thanks for the reality check. You are of course right. Though to @U0NCTKEV8 s point, i went in at 100k and checked the stack and it had only 4 frames with my method...which i suspect is one call with the clojure shenanigans.

👍 3
agile_geek11:11:30

Also even tho not using recur you are not holding on to the head of the collection so the collection discarded is garbage collected.