Fork me on GitHub
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…


are you using CIDER?

David Reno00:03:57

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


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

yes 3
❤️ 3
David Reno00:03:02

yep, cider/emacs


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?


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


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


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.


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


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


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


At least that is what is suggested by (in real world Clojure code I see it used in other ways)


It's a common enough question ^ @grazfather


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


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


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


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?


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


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


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...


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


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


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


Thank you for the thorough answer!


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 = =&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;   )


Are you asking how to do this in Clojure?


Just the allNearbyCells function.


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]
   (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})

👀 3
👍 6

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


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)


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


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)


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


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


Look for a websockets library that works on the client


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


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?


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


Is there a way to compare two data structures which are both a vector of hash-maps, [ {:a :one :b "start"} {:a :two :b "middle"} ,,,] when ordering is not the same? The use case is for a (probably quite dodgy) unit test Each vector has hash-maps that are the same set of values, however, as one vector of hash-maps is generated then ordering of the hash-maps is different I would prefer not to just try and sort the generated hash-map just to make the test work 🙂 Hmm, I could probably try sort the returned results in from the call in the test assertion... sorting them by the value of the string...


If it’s a dodgy test to begin with - is converting the vectors to sets an alternative?

👆 9

possibly... thats an interesting thought...


Unless you can have multiple identical maps :-)

👍 3

the hash-map has the same two keys but different values for each key. I could write a spec for it if I was doing it properly...

Jim Newton15:03:04

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


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

Jim Newton15:03:27

is it equal to 10?


(= 10 10N) true


([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.


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?


@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.


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


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 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 😄.


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.


I would like to understand better the deep-code walking macros/walks like in this post did you see similar article in this topic? Or github repo? Which is useful?



🍻 3

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.


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)


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!


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)


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


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


something like this (not tested):

(defmethod print-dup org.apache.http.impl.client.InternalHttpClient
  [o ^ 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?


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


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 !