Fork me on GitHub
#beginners
<
2023-11-19
>
Matthew Twomey02:11:47

(defn test1 [acc, v]
  (let [target (keyword (str/lower-case (str (first v))))]
   (update-in acc [target] conj v)))

(reduce test1 {} [ "Abby" "Bryan" "beakman"])
;; => {:a ("Abby"), :b ("beakman" "Bryan")}
I want “beakman” to come after “Bryan” in the output (e.g. essentially not reverse the order from the original list). Im fine if it’s vectors, but I’m unsure how to get it to be a vector in this case?

Bob B02:11:14

fnil lets you specify a first arg if the initial first arg is nil

(defn test1 [acc, v]
  (let [target (keyword (str/lower-case (str (first v))))]
    (update acc target (fnil conj []) v)))

Matthew Twomey02:11:57

woot! Thank you, I was not familiar with fnil. I appreciate you!

Bob B02:11:47

as a side note, this use case is a candidate for group-by:

(group-by (comp keyword str/lower-case str first) ["Abby" "Bryan" "beakman"])
=> {:a ["Abby"], :b ["Bryan" "beakman"]}

Matthew Twomey02:11:29

lol… perfect. So often I learn a better way here, thanks again.

👍 1
Carlos Sell05:11:11

Hello, I am using Calva in codespaces, but Alt+Enter to evaluate is not working.

pez05:11:24

Hi! Is the repl connected?

Carlos Sell06:11:23

I found the problem. For some unknown reason, this was added to the settings.json

"calva.keybindingsEnabled": false
I changed it to true, and now it works!

👍 1
pez08:11:08

Great! Maybe you happened to trigger the command Calva: Toggle Keybindings Enabled. We used to have a default keybinding for it, but that created a lot of confusion. 😃

Dean Gwilliam12:11:53

HI there I've got yesterday's calva but when trying to debug (F5) it says there's an extension missing and I don't fancy any of the list that gets presented. I'm on Manjaro. I saw an extension pack tab within the calva extension but its empty. Any help much appreciated and BTW...thanks for adding the icing on the cake to what looks to be an awesome set-up.

pez14:11:52

Hi! Calva’s debugger is not started like that. A Clojure program is always running, so in effect the debugger is always running. You need to place breakpoints for the debugger to stop at, one way is to instrument a form. See: https://calva.io/debugger/ That said. Very often you don’t need the debugger. Inline def’s cover a lot of that use case. Inline def means to place a (def x something) in a form, where something is a a local variable. I often do (def something something), even. There are tools for making this even more convenient, like snitch: https://github.com/AbhinavOmprakash/snitch

Dean Gwilliam16:11:04

Thank you very much indeed for your help

🙏 1
Dean Gwilliam18:11:29

Yep - all there and working great. Thanks once again.

clojure-spin 1
calva 1
ChillPillzKillzBillz14:11:02

Hello All, When using clj-http.client, I try to send a query like the following

(client/get "" {:debug true
                                         :query-params {:wiki "Hello,"}})
The request url truncates the , in the query-param to change it to the following request body
Request: nil
{:user-info nil,
 :use-header-maps-in-response? true,
 :body-type nil,
 :debug true,
 :headers {"accept-encoding" "gzip, deflate"},
 :server-port nil,
 :url "",
 :flatten-nested-keys (:query-params),
 :uri "",
 :server-name "en.wikipedia.org",
 :query-string "wiki=Hello%2C",
 :body nil,
 :scheme :https,
 :request-method :get}
HttpRequest:
{:config nil,
 :method "GET",
 :requestLine
 #object[org.apache.http.message.BasicRequestLine 0x49ee56b4 "GET  HTTP/1.1"],
 :aborted false,
 :params
 #object[org.apache.http.params.BasicHttpParams 0x702bd90c "[parameters={}]"],
 :protocolVersion
 #object[org.apache.http.HttpVersion 0x73e54376 "HTTP/1.1"],
 :URI
 #object[java.net.URI 0x6cdd1729 ""],
 :class org.apache.http.client.methods.HttpGet,
 :allHeaders
 [#object[org.apache.http.message.BasicHeader 0x1877c862 "Connection: close"],
  #object[org.apache.http.message.BasicHeader 0x14b74516 "accept-encoding: gzip, deflate"]]}
I was expecting the uri to be
"GET , HTTP/1.1"
Is this normal? Or am I doing something wrong? Also how should special characters like :,- be handled in requests? Any help will be much appreciated. Thanks! PS: This query has nothing to do with Adele! 😄

1
ChillPillzKillzBillz14:11:06

I've solved this... the only thing needed to fix this was to pass the Hello, within a call to str

Bob B15:11:16

The percent thing is normal for query params in a URL. For example, if you go to wikipedia, type 'hello,' in the search box, hit enter, and look at the URL at which you end up, you'll see (among other params) search=hello%2C. https://en.wikipedia.org/wiki/Percent-encoding has a longer discussion of this. Tangentially, I think if you mouse over the link previews, there will be an X that will let you remove the preview (if you want to banish the Adeles).

🙌 1
Audrius17:11:19

Please help - I am out of ideas:

(defn start-mac-gamepad []
  (let [proc (shell/sh "/usr/bin/python3" "read_gamepad.py")
        _ (prn "---01--- "proc)
        in (io/reader (:out proc))]
    (while true
      (let [line (.readLine in)]
        (prn "--- " line)))))
the line with shell/sh never returns even if both python3 and read_gamepad.py files exist and work from the command line.

hiredman17:11:50

sh attempts to capture all the output into a string and return it, so it needs to wait until the process exits

Matthew Twomey17:11:34

I am having trouble knowing how to understand if a function I write “maintains laziness”. For example this function here:

(defn remove-nth
  [coll n]
  (let [coll (seq coll)]
    (concat (take n coll) (drop (inc n) coll))))
I am uncertain if it’s returning a lazy sequence and furthermore, I am unsure how to “know” or figure this out in a more general sense. Any pointers here?

daveliepmann18:11:45

My general solution is that there's no shortcut: I just develop familiarity with the seq API and rely on jump-to-definition.

Matthew Twomey18:11:13

Ok.. So in this example, I’m thinking I should look at seq/concat/take/drop and if they all return lazy sequences, then my function should also be lazy. Is that a reasonable starting point?

Matthew Twomey18:11:58

I just did that, in this case - it appears that seq is not (necessarily?) lazy. So my use of it may be resulting in a non-lazy result.

Matthew Twomey18:11:10

But I just don’t really know how to know.

hiredman18:11:58

seq doesn't have anything to do with laziness other than it can be used to force a single layer of laziness to determine if a lazy seq is empty (nil) or not

hiredman18:11:13

In general the only time you need to call it is when you want to do something like (if (seq s) ... ...) Where what it is being used for is testing if s is empty

Matthew Twomey18:11:32

I am using it here so that the function will handle maps

Matthew Twomey18:11:00

e.g.

(remove-nth {:a 1 :b 2 :c 3 :d 4} 2)
;; => ([:a 1] [:b 2] [:d 4])

hiredman18:11:16

Drop and take internally call seq on their args

hiredman18:11:56

And that is the other thing seq does, it gets a seq over things that are not seqs

hiredman18:11:41

But most(all?) of the functions that operate on seqs do that coercion internally

Matthew Twomey18:11:44

Oh lol. Ok, I see that now. I thought I was getting an error without it, but I see now that I don’t need it (thanks). Updated function:

(defn remove-nth [coll index]
  (concat (take index coll) (drop (inc index) coll)))

Matthew Twomey18:11:50

But the laziness

Matthew Twomey18:11:02

I think it’s maintaining laziness. That’s my my question.

Matthew Twomey18:11:39

I just found realized? I think that may be what I need to confirm:

(def t1 (remove-nth {:a 1 :b 2 :c 3 :d 4} 2))
;; => #'cities/t1
cities> 
cities> (realized? t1)
;; => false

Matthew Twomey18:11:46

I think that means it’s still lazy.

hiredman18:11:00

realized is nonsense

hiredman18:11:19

I mean it sort of does stuff, but it is not useful

hiredman18:11:02

Because the thing about ISeq is it is modeled on a cons cell type of list, it is really the interface for a single cell

hiredman18:11:25

And as a consequence the laziness built on top of it exists at a cell at a time

hiredman18:11:52

So you can have a seq where the first item is "lazy" and the rest of the seq is not

Matthew Twomey18:11:26

Oh ok. Darn. Can you confirm if my function is lazy, e.g. do you know and if so - how do you know?

hiredman18:11:50

Another issue is chunked seqs

hiredman18:11:56

Strictly speaking clojure's lazy seqs are more like "non-strict seqs" in that there is no guarantee that if you walk it one element at a time that only one element at a time is realized

hiredman18:11:27

For some kinds of seqs you can have a kind of batching behavior where they realize in chunks of 32 elements

hiredman18:11:49

It is best to think of lazy seqs as an optimization the runtime can do to fuse multiple traversals into a single traversal, and to avoid caring about things being lazy or not

Matthew Twomey18:11:38

This actually came down to my desire to add a doc string to this function. concat has a doc string that says: “Returns a lazy seq …” I realized when writing my doc string here that I don’t know if my function is returning a “lazy sequence” (in the way that clojure core thinks about lazy sequences). So I am trying to document it accurately.

hiredman18:11:00

Concat returns a lazy sequence

Matthew Twomey18:11:10

Yes, but does my function?

hiredman18:11:34

A lazy sequence and the sequence being realized or not are different things

hiredman18:11:54

Your return the result of calling concat, concat returns a lazy sequence

hiredman18:11:37

If calling + returns a number, and your function returns the result of calling +, what does your function return?

Matthew Twomey18:11:01

It also calls take and drop, given all that would you say my function can still be described as “returns a lazy seq..” ?

hiredman18:11:30

Not sure what you are trying to say?

Matthew Twomey18:11:18

I am trying to understand if documenting my function as “Returns a lazy seq…” is an accurate statement. I wasn’t sure, because it’s using several functions like concat , take and drop.

hiredman18:11:29

a lazy seq is a data type, like a hash map

Matthew Twomey18:11:01

I do understand what it is, I was trying to understand if it’s accurate to say that my function returns a alzy sequence.

hiredman18:11:02

So your function might do all kinds of stuff internally, but if it returns a hash map it returns a hash map

Matthew Twomey18:11:21

I might be asking too dumb of a question. I’m starting to feel like to you, it’s obvious that my function returns a lazy sequence. Is that the case? (if so, I apologize, I was just trying to confirm)

hiredman18:11:41

It is very possible to write the same seq returning function with varying degrees of laziness(in how lazy the output is constructed in terms.of the input), but regardless what it returns is still a lazy seq

ghadi18:11:30

It’s also no lazier than required

ghadi18:11:38

There is a certain amount of essential work

Matthew Twomey18:11:46

So “yes”? It’s accurate to say this function:

(defn remove-nth [coll index]
  (concat (take index coll) (drop (inc index) coll)))
returns a lazy sequence?

ghadi18:11:48

(Peeling off the first n items)

hiredman18:11:36

Your function returns a lazy seq, but isn't a nice lazy seq because it needs to traverse it's input multiple times, but I don't know that you would be able to do any better

🙌 1
hiredman18:11:42

Something to keep in mind if you are passing maps in to that is maps are not ordered

👍 1
Matthew Twomey18:11:46

Ok. Thanks. I do really appreciate the deeper discussion, I’ve just never delved all that deeply into lazy -vs- realized sequences and I have a lot to catch up on there.

hiredman18:11:42

They have a fixed iteration order (in a single program run, a given map will always return a seq with elements in the same order) but that order is not defined

Matthew Twomey18:11:05

It might change with the next repl?

Matthew Twomey18:11:20

(I did know it wasn’t defined, I just didn’t know the internal mecahnics)

hiredman18:11:43

It generally doesn't because the order ends up being an implementation detail related to how hash codes are calculated

hiredman18:11:01

But clojure has changed hashing once in the past

hiredman18:11:40

array maps exist and are insertion ordered, but you generally should not depend on your maps staying an array map, most operations will promote from an array map to a hash when you have more than 8 items in the map

Matthew Twomey18:11:24

That implementation detail is for efficiency yes?

hiredman18:11:29

Array maps mostly exist (in my opinion) to preserve evaluation order of code in map literals

dpsutton20:11:54

something like

(defn remove-nth [n coll]
  (letfn [(iter [n coll]
            (when (seq coll)
              (if (zero? n)
                (next coll)
                (lazy-seq
                  (cons (first coll)
                        (remove-nth (dec n) (next coll)))))))]
    (iter n coll)))
could be a bit “lazier” in the sense it doesn’t traverse the original seq for the nth item during construction.

dpsutton21:11:34

ah. this is just doing exactly what the drop and take are doing. except just a single walk. so not saving on “laziness”

st02:11:19

Hello, not very scientific but what I do is try with infinite and take some elements : (first (remove-nth (range) 0)) You can also ask the type/class of a result (type (remove-nth (range) 0)) I have no idea if both tricks qualify for an absolute guarantee of lazyness in all cases.

hiredman02:11:35

A distinction drawn by some people who are very interested in different kinds of programming languages is between non-strict and lazy. Lazy is taken to mean what is required to produce the final result is evaluated and nothing else. Non-strict is taken to mean that more might be evaluated than strictly what is required.

🙌 2
hiredman02:11:32

strict is taken to mean the opposite of lazy everything is evaluated

hiredman02:11:09

So you can split quite fine hairs about it, and handling infinite values without blowing up is not enough to show laziness in that sense

hiredman02:11:08

Clojure is generally a lot less formal than all that in the terms that get thrown around