beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
alexmiller 2021-03-10T00:05:31.166700Z

Optional trailing map

seancorfield 2021-03-10T00:07:52.166900Z

So you'll declare with & {:keys [...] :as opts} and then you can call with either :named "arguments" or {:named "arguments"}? Sounds good.

2021-03-10T00:18:38.167900Z

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

2021-03-10T00:21:15.168Z

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

dpsutton 2021-03-10T00:21:31.168200Z

are you using CIDER?

2021-03-10T00:21:57.168400Z

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

dpsutton 2021-03-10T00:22:49.168600Z

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

❤️ 1
1
2021-03-10T00:23:02.168800Z

yep, cider/emacs

dpsutton 2021-03-10T00:23:45.169Z

and these are awesome as well:

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

2021-03-10T00:24:06.169200Z

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

dpsutton 2021-03-10T00:24:21.169400Z

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

2021-03-10T00:24:38.169600Z

thanks!

dpsutton 2021-03-10T00:24:59.169800Z

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

dpsutton 2021-03-10T00:25:49.170Z

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

2021-03-10T00:26:50.170200Z

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

dpsutton 2021-03-10T00:28:11.170400Z

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

grazfather 2021-03-10T01:56:24.171400Z

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

blak3mill3r 2021-03-10T01:57:52.171800Z

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

blak3mill3r 2021-03-10T01:58:51.172600Z

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)

alexmiller 2021-03-10T02:08:44.172900Z

or a mix of some args AND a trailing map

seancorfield 2021-03-10T02:16:09.173200Z

Oh, interesting. Do you think it'll start a major shift back to named arguments? Although I guess that will depend on how quickly folks adopt 1.11...

alexmiller 2021-03-10T02:18:44.173400Z

https://clojure.atlassian.net/browse/CLJ-2603 for more

2
seancorfield 2021-03-10T02:20:27.173700Z

https://clojure.org/guides/faq#qmark_bang

seancorfield 2021-03-10T02:20:55.174200Z

It's a common enough question ^ @grazfather

grazfather 2021-03-10T02:40:27.174700Z

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

seancorfield 2021-03-10T02:46:54.175300Z

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

grazfather 2021-03-10T02:47:56.175900Z

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

seancorfield 2021-03-10T02:55:01.178300Z

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?

seancorfield 2021-03-10T02:55:25.178800Z

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

seancorfield 2021-03-10T02:55:56.179500Z

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

seancorfield 2021-03-10T03:04:05.182500Z

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

seancorfield 2021-03-10T03:06:32.182900Z

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

seancorfield 2021-03-10T03:06:59.183400Z

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

seancorfield 2021-03-10T03:09:32.184Z

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

grazfather 2021-03-10T13:48:06.198200Z

Thank you for the thorough answer!

2021-03-10T05:11:14.186300Z

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 && y will be turned into numbers, returns array        let ID = theId.map(stringNumber => 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 <= x + 1; i++) {          for (let j = y - 1; j <= 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 && j === y) {              if (skipMiddle) {                continue;              }            }            fn(nestedArray[i][j]);          }        }      } ;     createNestedArray(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"}]]                   function createNestedArray (x, y) {        let nestedArray = [];        for (let i = 0; i < y; i++) {          nestedArray.push([]);          for (let j = 0; j < x; j++) {                        //information of individual cell            nestedArray[i][j] = {              id: `${j}-${i}`,            };          }        }        return nestedArray;   )

2021-03-10T06:18:59.186900Z

Are you asking how to do this in Clojure?

2021-03-10T06:27:02.187100Z

Just the allNearbyCells function.

solf 2021-03-10T07:04:24.187500Z

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

👍 2
👀 1
2021-03-10T07:07:23.187900Z

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

solf 2021-03-10T07:13:25.188400Z

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 Makhathini 2021-03-10T09:14:58.190100Z

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

Sithabiso Makhathini 2021-03-11T09:33:13.274100Z

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 Makhathini 2021-03-11T14:17:22.301800Z

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

jb recluse 2021-03-12T14:28:37.343900Z

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

jb recluse 2021-03-12T14:29:04.344100Z

for the zipfile i downloaded, see the resources section of https://pragprog.com/titles/dswdcloj3/web-development-with-clojure-third-edition/

Sithabiso Makhathini 2021-03-14T10:45:26.468200Z

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

raspasov 2021-03-10T09:20:42.190500Z

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 Makhathini 2021-03-10T09:26:01.190700Z

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

practicalli-john 2021-03-10T12:43:29.195700Z

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

javahippie 2021-03-10T12:50:10.196200Z

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

👆 3
practicalli-john 2021-03-10T12:51:02.196500Z

possibly... thats an interesting thought...

2021-03-10T13:39:16.197700Z

Unless you can have multiple identical maps :-)

👍 1
practicalli-john 2021-03-10T13:42:49.197900Z

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

dpsutton 2021-03-10T14:52:55.199800Z

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

dpsutton 2021-03-10T14:53:15.200500Z

Look for a websockets library that works on the client

Jim Newton 2021-03-10T15:41:04.201400Z

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

dpsutton 2021-03-10T15:42:01.201800Z

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

Jim Newton 2021-03-10T15:44:27.202Z

is it equal to 10?

dpsutton 2021-03-10T15:45:12.202200Z

(= 10 10N) true

Jim Newton 2021-03-10T15:45:27.202400Z

ah ha..

dpsutton 2021-03-10T15:45:42.202700Z

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.

dpsutton 2021-03-10T15:45:53.203Z

that's the type independent manner of =

Jakub Zika 2021-03-10T18:02:00.203400Z

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?

seancorfield 2021-03-10T18:04:08.204900Z

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

dpsutton 2021-03-10T18:04:29.205200Z

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

dpsutton 2021-03-10T18:07:07.207400Z

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 Zika 2021-03-10T18:13:34.212200Z

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

dpsutton 2021-03-10T18:14:08.212900Z

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

Jakub Zika 2021-03-10T18:25:04.216700Z

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

sb 2021-03-10T18:28:32.219600Z

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?

phronmophobic 2021-03-10T18:52:38.219800Z

reminds me of https://www.youtube.com/watch?v=HXfDK1OYpco

🍻 1
sb 2021-03-10T19:45:31.220500Z

Thanks!

🍻 1
sb 2021-03-10T19:46:17.221600Z

I really love Timothy Baldridge courses as at Pivotshare

Jakub Zika 2021-03-10T20:10:10.228Z

@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

dpsutton 2021-03-10T20:11:07.229300Z

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 Zika 2021-03-10T20:43:36.253700Z

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 Wellman 2021-03-10T21:08:31.257900Z

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)

blak3mill3r 2021-03-10T21:30:16.258600Z

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

blak3mill3r 2021-03-10T21:31:01.258800Z

See https://clojure.org/reference/reader#tagged_literals

Daniel Wellman 2021-03-10T21:31:37.259200Z

@blak3mill3r Thank you, and I appreciate the link!

blak3mill3r 2021-03-10T21:32:44.260400Z

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)

blak3mill3r 2021-03-10T21:33:51.261100Z

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

blak3mill3r 2021-03-10T21:35:33.261600Z

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

blak3mill3r 2021-03-10T21:36:32.262300Z

something like this (not tested):

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

Daniel Wellman 2021-03-10T21:36:50.262800Z

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

blak3mill3r 2021-03-10T21:37:37.263500Z

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

blak3mill3r 2021-03-10T21:38:03.263900Z

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 Wellman 2021-03-10T21:41:40.264100Z

That makes sense, thanks !