Fork me on GitHub
#beginners
<
2021-03-10
>
David Reno00:03:38

I’m a little stuck on this, it returns nil but prints nothing. I’d have expected it to print 10. Any thoughts?

(let [timeout-channel-10 (a/timeout 10)
      timeout-channel-20 (a/timeout 20)
      [m c] (a/alts!! [timeout-channel-10 timeout-channel-20])]
  (if (= c timeout-channel-10)
    (println 10)
    (println 20))))

David Reno00:03:15

oh my goodness, it’s the extra paren. I’ve been executing the hosting comment block…

dpsutton00:03:31

are you using CIDER?

David Reno00:03:57

apparently I’ve been staring at the screen too long…

dpsutton00:03:49

if this is emacs (setq clojure-toplevel-inside-comment-form t) is quite helpful for this

yes 1
❤️ 1
David Reno00:03:02

yep, cider/emacs

dpsutton00:03:45

and these are awesome as well:

(setq cider-invert-insert-eval-p t)
(setq cider-switch-to-repl-on-insert nil)

David Reno00:03:06

I’ll have to look these all up but apparently they save me from myself?

dpsutton00:03:21

these would a) evaluate the top leve inside of the comment form. and the latter two make it easier to send forms to the repl

dpsutton00:03:59

so i constantly use cider-send-form-to-repl and i make sure that "top level" does what i want, it doesn't focus the repl when i do it, and it evals the form

dpsutton00:03:49

check out the map under C-c C-j d for sent top level form to repl. e for last expression

David Reno00:03:50

I tend to only use “cider-eval-last-sexp” but I’ll look at the ones you mention.

dpsutton00:03:11

sure. the top level stuff is super useful for forms inside of a comment. you don't have to move to the end of the form, just anywhere inside of the form

grazfather01:03:24

What does ! at the end of a function suggest? I am confused why set-validator has it but add-watch doesn’t

blak3mill3r01:03:52

It means that it is not idempotent and therefore should not ever be run inside a transaction

blak3mill3r01:03:51

At least that is what is suggested by https://github.com/bbatsov/clojure-style-guide#unsafe-functions (in real world Clojure code I see it used in other ways)

seancorfield02:03:55

It's a common enough question ^ @grazfather

grazfather02:03:27

Thanks. Is set-validator! vs add-watch just an inconsistency, then?

seancorfield02:03:54

@grazfather add-watch is additive, set-validator! overwrites.

grazfather02:03:56

But wouldn’t add-watch overwrite with a watcher with a matching key? or is the key ONLY used to provide to the function?

seancorfield02:03:01

True, it does, but the docstring reads as if you are responsible for ensuring the keys are unique for a given reference so maybe that aspect is considered "undefined" behavior?

seancorfield02:03:25

Either way, most set* functions have ! and most add* functions don't.

seancorfield02:03:56

However, if you repeatedly add-watch on the same key with the same function, that's idempotent.

seancorfield03:03:05

add-watch does an assoc on internal state so it's safe to call multiple times. set-validator! actually derefs the ref and runs the validator on it, and then sets the validator into the ref -- so that could throw an exception...

seancorfield03:03:32

So, yeah, I guess that justifies the ! on it.

seancorfield03:03:59

(sorry for the slow series of responses, I'm grubbing around in the source code of it all)

seancorfield03:03:32

(and it's worth repeating that these are just conventions rather than hard and fast rules)

grazfather13:03:06

Thank you for the thorough answer!

fullNameHere05:03:14

Heys guys, ive been learning clojure for a couple months now, and really been liking it. for the most part I could do things the clojure way. ;But I cant figure this out. Most of it is Js, so sorry in advance. ; ;Im referring to the allNearbyCells function. ;In createNestedArray (3, 3), given cellId "1-1", it would modify all adjecent objects. ;given "0-0" it would return the top 4 left. so given the middle it would modify all adjacent cells. weather it modifies or returns a new  ;object/making it more functional doesnt really matter. Mostly the for loops im trying to get rid of, but cant really think of a clean way of doing it.   stringToId: function(string) {        //given 'x-y' will return ['x', 'y']        let theId = string.split("-");        //x &amp;&amp; y will be turned into numbers, returns array        let ID = theId.map(stringNumber =&gt; parseInt(stringNumber));        return ID;      }  allNearbyCells: function(cellId, nestedArray, fn, skipMiddle) {        //skipMiddle defaults to false, true applys function passed in to cellId        let Id = this.stringToId(cellId);        const y = Id[0];        const x = Id[1];        for (let i = x - 1; i &lt;= x + 1; i++) {          for (let j = y - 1; j &lt;= y + 1; j++) {            //if it dont exist, move on            if (!nestedArray[i] || !nestedArray[i][j]) {              continue;            }            //skip the cell that was passedd in            if (i === x &amp;&amp; j === y) {              if (skipMiddle) {                continue;              }            }            fn(nestedArray[i][j]);          }        }      } ;     createNestedArray(3, 3) -&gt; [[{id: "0-0"}, {id: "1-0"}, {id: "2-0"}], ;                                 [{id: "0-1"}, {id: "1-1"}, {id: "2-1"}],  ;                                  [{id: "0-2"}, {id: "1-2"}, {id: "2-2"}]]                   function createNestedArray (x, y) {        let nestedArray = [];        for (let i = 0; i &lt; y; i++) {          nestedArray.push([]);          for (let j = 0; j &lt; x; j++) {                        //information of individual cell            nestedArray[i][j] = {              id: `${j}-${i}`,            };          }        }        return nestedArray;   )

andarp06:03:59

Are you asking how to do this in Clojure?

fullNameHere06:03:02

Just the allNearbyCells function.

solf07:03:24

Here's a mostly literal translation of your code in clojure:

(ns cells
  (:require [clojure.string :as str]
            [clojure.edn :as edn]))

;; Using edn
(defn string->id [s]
  (map edn/read-string
       (str/split s #"-")))

;; Using java Integer/parseInt
(defn string->id [s]
  (map #(Integer/parseInt %)
       (str/split s #"-")))

(string->id "1-2")
;; => (1 2)

(defn create-nested-array [x y]
  (partition
   x
   (for [i (range x)
         j (range y)]
     {:id (str j "-" i)})))

(create-nested-array 3 3)
;; (({:id "0-0"} {:id "1-0"} {:id "2-0"})
;;  ({:id "0-1"} {:id "1-1"} {:id "2-1"})
;;  ({:id "0-2"} {:id "1-2"} {:id "2-2"}))

(defn all-nearby-cells [id nested-array f skip-middle]
  (let [[y x] (string->id id)
        dir   [-1 0 1]]
    (map (or f identity)
         (filter some?
                 (for [x-dir dir
                       y-dir dir
                       :let [x2 (+ x x-dir)
                             y2 (+ y y-dir)]
                       :when (not (and skip-middle
                                       (= x x2)
                                       (= y y2)))]
                   (-> nested-array
                       (nth y2 nil)
                       (nth x2 nil)))))))

(all-nearby-cells "0-0" (create-nested-array 3 3) nil false)
;; => ({:id "0-0"} {:id "0-1"} {:id "1-0"} {:id "1-1"}) 

(all-nearby-cells "0-0" (create-nested-array 3 3) nil true)
;; => ({:id "0-1"} {:id "1-0"} {:id "1-1"})

(all-nearby-cells "0-0" (create-nested-array 3 3) #(assoc % :found true) false)
;; => ({:id "0-0", :found true}
;;     {:id "0-1", :found true}
;;     {:id "1-0", :found true}
;;     {:id "1-1", :found true})

👀 1
👍 2
fullNameHere07:03:23

Haha good stuff. Thank you, I couldn't figure it out.

solf07:03:25

My nested array is not actually a nested array, but a nested list, which isn't ideal here. With arrays you could use get-in, here I used two nth instead (which is O(n) for sequences)

Sithabiso Makhathini09:03:58

Anybody have any idea why I am getting this error for shadow-cljs? P.S "lein run" works perfectly.

raspasov09:03:42

I am not a shadow-cljs expert by any stretch but I believe you’re trying to require http-kit from a ClojureScript namespace. I believe http-kit is a JVM-only library (no JS/ClojureScript)

Sithabiso Makhathini09:03:01

Oh I see, the reason why I had it there is because I would like to make use of websockets and when I dont have it there I dont have access to them

dpsutton14:03:55

You don’t have access to them there with that lib there. It uses a bunch of Java libraries. There’s no way to compile that to js

dpsutton14:03:15

Look for a websockets library that works on the client

Sithabiso Makhathini09:03:13

Okay a bit of context might be a step in the right direction. I am currently going through the "web development with clojure 3rd edition" book and thats how I ran into this error. I went through all the steps and rewrote the code but I am still unsure of where I went wrong, heres a screenshot of what I get when I remove the ws namespace

Sithabiso Makhathini14:03:22

I am using sente now but I still don't have access to "ws"

jb recluse14:03:37

hi, i dont have this book but i did find the reference code. are you requiring guestbook.websockets :as ws in core.cljs?

Sithabiso Makhathini10:03:26

@U017JCS7L2Y brother i literally had to require guestbook.websockets :as ws bro thank you so much

Jim Newton15:03:04

what does it mean if a number is printed as 10N ?

dpsutton15:03:01

(type 10N) #_ -> clojure.lang.BigInt

Jim Newton15:03:27

is it equal to 10?

dpsutton15:03:12

(= 10 10N) true

dpsutton15:03:42

clojure.core/=
([x] [x y] [x y & more])
  Equality. Returns true if x equals y, false if not. Same as
  Java x.equals(y) except it also works for nil, and compares
  numbers and collections in a type-independent manner.

dpsutton15:03:53

that's the type independent manner of =

Jakub Zika18:03:00

Hello, I am using next.jdbc to query postgresql. Then I save results into EDN file. Numbers in range - 2147483648 and 2147483648 are coming as *int*s from jdbc (because the column is type int4 and not int8). But once I read the EDN fila via (read-string (slurp "table.edn")) any number > 10 is changed from int to long because EDN integers are in range 0-10. What is the most idiomatic way to read these numbers and *int*s again?

seancorfield18:03:08

@jakub.zika-extern Clojure defaults to Double and Long for numeric types. You would need to cast to a narrower type if you really wanted Integer or int instead of Long.

dpsutton18:03:29

a minimal example of what you want is (type (clojure.edn/read-string "3")) to return an Integer or int?

dpsutton18:03:07

i don't see a way to do that at the reader level. you could walk the data obviously. but why do you specifically need ints?

Jakub Zika18:03:34

@seancorfield @dpsutton I am using https://github.com/jgdavey/clj-pgcopy to migrate postgres database. It maps int to int4 and long to int8 - as jdbc reads. I have got a lot of int4 in my database and when I use EDN and everything is casted to longs it fails with incorrect binary data format. One of the obvious solutions is changing every column from int4 in db to int8 😄.

dpsutton18:03:08

in your insert statements can you do the cast there? dealing with concrete types in Clojure can often be quite challenging

Jakub Zika18:03:04

I dont think so. I would use next.jdbc engine which solves my problem but it seems much slower than clj-pgcopy library.

sb18:03:32

I would like to understand better the deep-code walking macros/walks like in this post http://blog.fogus.me/2013/07/17/an-introduction-to-deep-code-walking-macros-with-clojure/ did you see similar article in this topic? Or github repo? Which is useful?

sb19:03:31

Thanks!

🍻 1
sb19:03:17

I really love Timothy Baldridge courses as at Pivotshare

Jakub Zika20:03:10

@dpsutton I see that I can use option when read-string EDN so I might tag some value like #int4 and then convert it back to type i need. https://clojuredocs.org/clojure.edn/read-string

dpsutton20:03:07

i don't see how that's easier than all of your insert statements mapping longs to ints. or redefining the target type of longs for this project

Jakub Zika20:03:36

I have got two databases sharing the same structure. I want to move some data from the 1st database to the 2nd one. There are queries (SELECT * from table limit 100) which creates <file>.edn for every extracted table. Then I want to use pgcopy (clj-pgcopy) to insert these data quickly. If I remap longs to ints - it may not be always possible. If I redefine the target type of longs - it will fail once there will be int8 on db side. The point is - I can not lose the information about data type coming from first db.

Daniel Wellman21:03:31

I’m experimenting with writing a test where I use with-redefs to replace clj-http.client calls using a record live/replay a file pattern. I try serializing the captured response with pr-str and save it to disk, then load it again in replay mode using read-string. However, this fails with a No reader function for tag object because the Apache HTTP client gets serialized, e.g. :`http-client #object[org.apache.http.impl.client.InternalHttpClient 0x12079bbf …]` . Is there a way to provide a hint to the reader to ignore this tag? (Maybe a second question is: “Is there another library in Clojure for this style of testing or record/replay?” In the past I’ve used WireMock which just occurred to me would work, though be a bit slower due to the real HTTP traffic)

blak3mill3r21:03:16

You can do that @etldan by defining a reader function that does nothing

Daniel Wellman21:03:37

@blak3mill3r Thank you, and I appreciate the link!

blak3mill3r21:03:44

you just need to put a data_readers.clj file at a root of your classpath, containing a map from symbols (reader shorthands) to symbols (fully-qualified functions that will read a form the way you want, which in your case is to return nil)

blak3mill3r21:03:51

and then control the way they are printed, to match your reader shorthands (see print-dup and print-method from clojure.core)

blak3mill3r21:03:33

actually @etldan you might be able to get away with just extending the printing to print a blank string... which the reader will skip

blak3mill3r21:03:32

something like this (not tested):

(defmethod print-dup org.apache.http.impl.client.InternalHttpClient
  [o ^java.io.Writer w]
  (print-simple "" w))

Daniel Wellman21:03:50

Thank you @blak3mill3r I’ll look into this. I’m assuming these are global - could they be temporarily redefined such as like with-redefs?

blak3mill3r21:03:37

yeah multimethod definitions are global... I am not sure if they can be used with with-redefs

blak3mill3r21:03:03

of course you could make the body of the function do whatever you want, including calling another dynamically bound function to do the actual work

Daniel Wellman21:03:40

That makes sense, thanks !