beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Joe 2021-01-14T00:41:40.002700Z

Just watched Sean’s REPL talk using VS Code and Clover. If I’m pretty happy with my Calva set up is there any argument in favour of switching? Or is more a personal preference / devil you know sort of thing?

seancorfield 2021-01-14T00:43:17.003900Z

@allaboutthatmace1789 Mostly personal preference -- as I said in the talk, I want a setup that works with just a socket REPL and no additional dependencies in my project, so I have the exact same experience with any Clojure process running (since I can start a socket REPL via a JVM option -- not code).

seancorfield 2021-01-14T00:43:45.004400Z

Everything I showed can be done with (almost) any other editor/integration setup.

Joe 2021-01-14T00:44:24.005100Z

That makes sense, thanks!

zackteo 2021-01-14T05:50:38.008800Z

Hi everyone, am wondering what is the best way to approach this problem I have. I have a list of data like so '({ :lrow 0, :col 0, :lcol 2, :row 0 } { :lrow 2, :col 0, :lcol 1, :row 1 } { :lrow 2, :col 2, :lcol 2, :row 1 })) my function will receive data like ...

{:coord {:row 0, :col 0, :sheet "Sheet1"},
           :value "ABC",
           :formula nil,
:style {:bold true}}
and I want to transform the data to be the following (when :row and :col correspond to an entry in the list) Aka add in :lrow and :lcol
{:coord {:row 0, :col 0,
         :lrow 0, :lcol 2,
         :sheet "Sheet1"},
           :value "ABC",
           :formula nil,
:style {:bold true}}
What might be the best way to approach this?

zackteo 2021-01-14T05:55:12.008900Z

I thought of using sets but doesn't fit exactly(?)

phronmophobic 2021-01-14T06:05:58.009300Z

if you construct a map so that you can look up the lrow,lcol coordinates by row, col

zackteo 2021-01-14T06:06:43.009500Z

what do you mean? 😮

phronmophobic 2021-01-14T06:06:55.009700Z

it should be easy to check the map with the row, col of the :coord map to add lrow and lcol if it exists

phronmophobic 2021-01-14T06:07:08.009900Z

you want to construct a map like:

{[0 0] {:lrow 0, :col 0, :lcol 2, :row 0},
 [1 0] {:lrow 2, :col 0, :lcol 1, :row 1},
 [1 2] {:lrow 2, :col 2, :lcol 2, :row 1}}

phronmophobic 2021-01-14T06:10:30.010100Z

so you can do something like:

(def coord-index {[0 0] {:lrow 0, :col 0, :lcol 2, :row 0},
                  [1 0] {:lrow 2, :col 0, :lcol 1, :row 1},
                  [1 2] {:lrow 2, :col 2, :lcol 2, :row 1}})

(def test-val {:coord {:row 0, :col 0,
                       :lrow 0, :lcol 2,
                       :sheet "Sheet1"},
               :value "ABC",
               :formula nil,
               :style {:bold true}})

(let [{{:keys [row col]} :coord} test-val]
  (if-let [{:keys [lrow lcol]} (get coord-index [row col])]
    (update-in test-val [:coord]
               assoc :lrow lrow :lcol lcol)
    test-val))

zackteo 2021-01-14T06:21:59.010400Z

Cool! That destructuring really helps I forgot that I could do that! And didn't think/know that I can use the row and col as an index in such a way And wasn't sure how to do the update

zackteo 2021-01-14T06:22:04.010600Z

Thanks a bunch!

zackteo 2021-01-14T06:22:12.010800Z

Gotta slow digest it

phronmophobic 2021-01-14T06:23:47.011Z

the example code I wrote is pretty dense. let me know if it would help if I unpacked it more.

👍 1
zackteo 2021-01-14T06:27:38.011200Z

Managed to unpack it once I figured out how the destructuring worked :)

😄 1
Sergej Koščejev 2021-01-14T07:36:50.011700Z

I have a file looking like this:

(ns stuff)

(ns stuff-test (:require stuff) (:use clojure.test))

(with-test
  (defn sum [s]
    (+ s 2))
  (is (= 2 (sum 1))))
and I'm asking CIDER to run all tests in project. But it's telling me that there are no tests. What am I doing wrong?

zackteo 2021-01-14T08:27:27.011800Z

Your test file test/project/core_test.clj should look like

(ns project.core-test
  (:require [clojure.test :refer :all]
            [project.core :refer :all]))

(deftest a-test
  (testing "FIXME, I fail."
    (is (= 0 1))))
And your main file doesn't need any test stuff

zackteo 2021-01-14T08:29:49.012Z

Not too sure where you got with-test from

2021-01-14T08:41:10.012200Z

cider by default is looking for deftest or defspec in outer forms of namespace. then it extract a name associated with that form and run (test (var symbol)) that means with-test is not tracked by cider at all

2021-01-14T08:41:56.012600Z

consider to use deftest

2021-01-14T08:45:17.012900Z

also multiple ns is not really a proper way to create namespaces

(ns stuff)

(defn x [] "!")

(ns sandbox.foo)

(defn x [] "@")

(x)

(stuff/x)
this is working but I believe will break namespace loading (eg. requiring stuff from another namespace)

Sergej Koščejev 2021-01-14T09:13:04.014500Z

I changed the file to this:

(ns stuff)

(ns stuff-test (:require stuff) (:use clojure.test))

(defn sum [s]
  (+ s 2))

(deftest addition
  (is (= 2 (sum 1))))
But the tests are still not being run, with the same message ("No assertions (or no tests) were run.Did you forget to use 'is' in your tests?")

Sergej Koščejev 2021-01-14T09:13:50.014700Z

re multiple ns: deleting the (ns stuff) above doesn't help either. (I wanted to have multiple namespaces in one file initially and split them later.)

Sergej Koščejev 2021-01-14T09:20:56.016700Z

ok it actually helped but only after I restarted CIDER

Sergej Koščejev 2021-01-14T09:21:09.016900Z

so

(ns stuff-test (:use clojure.test))

(with-test
  (defn sum [s]
    (+ s 2))
  (is (= 2 (sum 1))))
works

roelof 2021-01-14T09:08:45.013300Z

Hello,

roelof 2021-01-14T09:08:59.013700Z

I have this as response from a api

({:levels
  [{:name "z1",
    :width 2810,
    :height 2177,
    :tiles

roelof 2021-01-14T09:09:31.014400Z

how can I now takes the levels parts out of it and then filter for the vector where the name is z6 ?

Idan Melamed 2021-01-14T09:19:22.016300Z

Might be a more idiomatic way, but here's my solution: (filter #(= "z6" (:name %)) (:levels (first a)))

Idan Melamed 2021-01-14T09:23:23.017300Z

You can also do it with the thread last macro: (def a '({:levels [{:name "z1",:width 2810,:height 2177, :tiles 12}]})) (->> a first :levels (filter #(= "z6" (:name %))))

Franklin 2021-01-14T13:08:07.018500Z

is there a more idiomatic way to write (first (filter filter-function sequence))

Idan Melamed 2021-01-14T13:10:39.018600Z

Maybe the thread last macro? ->> sequence (filter filter-function) first

👍 1
Franklin 2021-01-14T13:16:15.019100Z

this seems all right... I was just wondering if there's a built-in function that I don't know about that. seems there isn't

2021-01-14T13:19:39.019300Z

Ad built-in function: such a proposal was rejected at some point -- see https://clojure.atlassian.net/browse/CLJ-2056?focusedCommentId=12986

👍 2
2
2021-01-14T13:24:18.019500Z

(some #(when (filter-function %) %) sequence)
but this will fail if it suppose to return false

👍 1
2021-01-14T14:29:51.020200Z

The small library medley contains find-first, and several other useful small functions: https://github.com/weavejester/medley

👍 1
Marcus 2021-01-14T14:47:22.020800Z

What is the difference between >! and put!?

alexmiller 2021-01-14T14:49:02.021800Z

the first is parking (used in go blocks only) - ie park the go block until the operation can succeed. latter is async, can be used from either in or out of go block

alexmiller 2021-01-14T14:49:41.022400Z

in most cases, if you are in a go block, you should prefer the parking operation (the async ops are lower level)

Marcus 2021-01-14T15:14:26.026600Z

@alexmiller ah of course. For some reason I thought of parking and async as the same thing. Thanks!

Mitch 2021-01-14T15:58:51.030400Z

This example fails to generate:

(require '[clojure.spec.alpha :as s])

(s/def :person/id string?)
(s/def :robot/id number?)

(s/def ::person
  (s/keys :req-un [:person/id]))

(s/def ::robot
  (s/merge ::person
           (s/keys :req-un [:robot/id])))

(gen/sample (s/gen ::robot))
Is this behavior expected? I thought that one of the benefits of merging map specs would be that you could overwrite defaults specs for certain keys

alexmiller 2021-01-14T16:07:29.030700Z

no, that's not expected to be supported

alexmiller 2021-01-14T16:07:41.031Z

you've written a spec that has incompatible options for the same key

Mitch 2021-01-14T16:16:18.034700Z

Okay, thanks! So if I understand correctly, the spec merge is semantically different than core merge. Core merge overwrites keys left to right, while spec merge tries to take the union of the map specs being merged. Sometimes, a union of specs can create an impossible to satisfy predicate

Alice Kile 2021-01-14T16:16:49.034900Z

👋

👋 2
Mitch 2021-01-14T16:18:03.035400Z

Is the union of two specs achieved using s/and?

alexmiller 2021-01-14T16:18:15.035700Z

Correct

alexmiller 2021-01-14T16:18:41.036400Z

If you want something that satisfies both, yes

alexmiller 2021-01-14T16:18:55.036900Z

But note that s/and flows conformed values

alexmiller 2021-01-14T16:19:30.037700Z

So using it on maps may not be what you want (it depends)

alexmiller 2021-01-14T16:20:39.039300Z

Spec 2 will have an s/and variant that does not flow conformed values (currently called s/and- but maybe to be called s/union)

alexmiller 2021-01-14T16:21:26.040100Z

Just FYI, there is #clojure-spec for more detailed spec questions

Mitch 2021-01-14T16:21:54.040700Z

Thanks Alex! I am going to make a note to document this on clojureDocs when I have a moment. Super helpful

2021-01-14T17:02:11.040900Z

Oh, http://ClojureDocs.org does have pages for each function in Clojure spec now (and probably has for a while now). I just didn't notice them.

Eamonn Sullivan 2021-01-14T17:53:09.049900Z

I have a really basic question about spec. I have a library where I define some specs (s/def). They are used in the library for various things and they work fine. However, I would also like to use some of the specs from outside the library (as options or config validation, for example), but I can't figure out how to use the spec outside of the original library. Contrived example (excuse the syntax errors):

(ns my.lib (:require [clojure.spec.alpha :as s]))
(s/def ::is-thingy string?)
(defn publicfn [thing] {:pre [(s/valid? ::is-thingy thing)]} ,,,) ;; this works fine
In client:
(ns my.client (:require [my.lib :as lib] [clojure.spec.alpha :as s])

(defn my-code [options] (when (s/valid? ::lib/is-thingy (:thing options)) ,,,)) ;; this doesn't. "Unable to resolve spec: :my.lib/is-thingy.
How do I refer to the spec definitions created in the library, outside of that library?

alexmiller 2021-01-14T17:55:24.050400Z

:my.lib/is-thingy is the fully-qualified name of your spec

alexmiller 2021-01-14T17:57:42.051100Z

::lib/is-thingy should work as a way of creating an alias - your code works for me

1
Eamonn Sullivan 2021-01-15T09:36:49.141900Z

🤦 I didn't run clojure -X:jar before installing the library...

Eamonn Sullivan 2021-01-14T18:03:20.051700Z

Thanks. Must be some other issue I'm having then. Maybe it's not resolving the correct library.

GGfpc 2021-01-14T19:15:10.054200Z

Is multiple maps in sequence in a threaded macro a smell?

(defn get-game-data
  [user-id]
  (let [url (str BASE_URL user-id)
        request-headers {:headers      {:accept "application/x-ndjson"}
                         :query-params {:max 20}}]
    (as-> (client/get url request-headers) games
          (:body games)
          (str/split games #"\n")
          (map json/read-str games)
          (map walk/keywordize-keys games)
          (map #(update-in % [:moves] str/split #" ") games))))
Stuff like the above. I could make it a single map, but then the code wouldn't be as clean

benny 2021-01-14T19:18:11.055200Z

@seancorfield Thank you for your Clojure London talk on REPL-Driven Programming. I learned quite a bit! (ref for others: https://www.youtube.com/watch?v=gIoadGfm5T8)

seancorfield 2021-01-14T19:18:31.055500Z

Glad it was useful!

2021-01-14T19:23:46.055600Z

seems fine to me. I guess I would usually reach for -> or ->> before as->. some people really don't like as-> used outside of another arrow form (I don't agree with this)

seancorfield 2021-01-14T19:32:47.055800Z

I'm one of those "some people". I think using as-> there makes it much harder to read...

alexmiller 2021-01-14T19:34:22.056Z

if you're talking about 10s or maybe even 100s of things, then I wouldn't worry about it, clarity wins. if trying improve perf, better to move to transducers and then multiple maps is also not a big deal.

seancorfield 2021-01-14T19:34:28.056200Z

(-> (client/get url request-headers)
    :body 
    (str/split #"\n")
    (->> (map json/read-str)
         (map walk/keywordize-keys)
         (map #(update-in % [:moves] str/split #" ")))
That is much clearer to my eyes.

seancorfield 2021-01-14T19:35:28.056500Z

What this highlights is that you're changing the data type in the middle of the pipeline (from a "thing" to a sequence).

seancorfield 2021-01-14T19:36:13.057Z

At which point, this applies: https://stuartsierra.com/2018/07/06/threading-with-style

seancorfield 2021-01-14T19:37:02.057300Z

And a follow-up post specifically about as->: https://stuartsierra.com/2018/07/15/clojure-donts-thread-as

2021-01-14T19:38:26.058100Z

Yes, it was good 🙂 @vlaaad needs to take down his xmas tree though...👀🌲

vlaaad 2021-01-14T19:40:52.058200Z

It's so snowy here in Stockholm, finally some Christmas weather!

roelof 2021-01-14T19:51:19.059700Z

I have this code :

(->> (read-all-data)
     (filter-artObjects)
     (filter-all-objectNumbers)
     (ask-for-image-data)
     (map filter-image-url))
which produces this :
{:name "z5"
  :width 146
  :height 179
  :tiles
  [{:x 0
    :y 0
    :url
    "<http://lh3.googleusercontent.com/dgdeRabPYriIi7v5R3fAk6cudxssTV_oEVaXz1zSo9UPlTcUPAJgZan9jhCfAPzjwtpDTb0v5vSja0uyrSR4LatmTw=s0>"}]})
clj꞉paintings.core꞉&gt; 
is there a way I can make it output like this : {width: xx , height: xx url: xxx} ` ?

roelof 2021-01-15T09:08:14.141600Z

could be, but then I have to take them out when displaying with hiccup. Maybe I have to reconsider my code because later I would like to add the objectnumber I get from a much earlier code to the map. Right now it seems to be impossible

roelof 2021-01-14T19:51:33.060Z

@vlaaad

roelof 2021-01-14T19:51:49.060600Z

here in the netherlands no snow the whole winter 🙂

vlaaad 2021-01-14T20:06:02.060700Z

It was like that here last year

roelof 2021-01-14T20:07:42.060900Z

but maybe net Sunday we have a few hours snow

2021-01-14T20:14:33.061100Z

how many tiles do you expect? always 1?

roelof 2021-01-14T20:16:17.061300Z

yep

2021-01-14T20:19:02.061500Z

(defn lift-first-tile [image]
  (let [tile (-&gt; image :tiles first)]
    (-&gt; image
        (dissoc :tiles)
        (merge tile))))

(lift-first-tile
 {:name "z5"
  :width 146
  :height 179
  :tiles
  [{:x 0
    :y 0
    :url
    "<http://lh3.googleusercontent.com/dgdeRabPYriIi7v5R3fAk6cudxssTV_oEVaXz1zSo9UPlTcUPAJgZan9jhCfAPzjwtpDTb0v5vSja0uyrSR4LatmTw=s0>"}]})
;; =&gt; {:name "z5",
;;     :width 146,
;;     :height 179,
;;     :x 0,
;;     :y 0,
;;     :url
;;     "<http://lh3.googleusercontent.com/dgdeRabPYriIi7v5R3fAk6cudxssTV_oEVaXz1zSo9UPlTcUPAJgZan9jhCfAPzjwtpDTb0v5vSja0uyrSR4LatmTw=s0>"}

roelof 2021-01-14T20:21:19.061700Z

??? now I get everything. I only need the 3 i have said

2021-01-14T20:22:53.061900Z

try select-keys, in that case 🙂

2021-01-14T20:25:52.062100Z

another approach:

(defn lift-first-tile-url [image]
  (let [url (-&gt; image :tiles first :url)]
    (assoc image :first-tile/url url)))

(lift-first-tile-url {:name "z5"
                      :width 146
                      :height 179
                      :tiles
                      [{:x 0
                        :y 0
                        :url "http:<http://google.com|google.com>"}]})
;; =&gt; {:name "z5",
;;     :width 146,
;;     :height 179,
;;     :tiles [{:x 0, :y 0, :url "http:<http://google.com|google.com>"}],
;;     :first-tile/url "http:<http://google.com|google.com>"}

roelof 2021-01-14T20:26:45.062300Z

(defn filter-image-url [imagedata]
  (-&gt;&gt; imagedata
       :levels
       (sort-by :name)
       (last))
        (select-keys [:width :height]))
; Error printing return value (ArityException) at clojure.lang.AFn/throwArity (AFn.java:429).
; Wrong number of args (1) passed to: clojure.core/select-keys

2021-01-14T20:29:30.062500Z

a few things jump out at me. first, i think there's a misplaced close parens in the example above - last)) closes the thread last macro before including the call to select-keys. so that may not be what you want

2021-01-14T20:30:32.062700Z

second, select-keys expects the map in the first position and the key sequence in the second, but the thread last macro -&gt;&gt; will feed imagedata into the last argument of select-keys

2021-01-14T20:30:59.062900Z

you can play with macroexpand-1 to see that:

(macroexpand-1 '(-&gt;&gt; imagedata
                     :levels
                     (sort-by :name)
                     (last)
                     (select-keys [:width :height])))
;; =&gt; (select-keys [:width :height] (last (sort-by :name (:levels imagedata))))

2021-01-14T20:31:19.063100Z

does that help?

Santiago 2021-01-14T20:34:15.066Z

I need to write something that 1) subscribes to a rabbitmq queue 2) gets a message and writes it to a TCP socket 3) TCP socket must be always open otherwise client (a Spark cluster) will close. AFAIK I need two loops concurrently: 1 to subscribe to rabbitmq and the other to serve the TCP socket. I have never done such a thing and would like to try doing in clojure. What literature do you recommend for such a problem?

2021-01-14T20:39:27.068200Z

Depending on a tcp socket to always be open is usually a bad idea

roelof 2021-01-14T20:40:41.068900Z

to see where the problem is yes, to see how to solve it not at the moment

Santiago 2021-01-14T20:41:04.069500Z

I think it won’t work otherwise, but in any case could you expand on why? I haven’t had experience with these topics at such a low level

2021-01-14T20:42:25.070100Z

Network hiccups are surprisingly common

Santiago 2021-01-14T20:42:34.070300Z

maybe “always open” is not the correct nomenclature

2021-01-14T20:42:46.070500Z

part of the difficulty can be managing the thread first vs thread last. thread last is good at handling collections but thread first is good at handling associative data structures. if you have an existing threading macro and you want to add an expression that requires the "opposite" argument placement, try writing a function

2021-01-14T20:42:55.070700Z

i'll show an example in a second

roelof 2021-01-14T20:43:36.070900Z

oke

Santiago 2021-01-14T20:44:04.071100Z

https://spark.apache.org/docs/latest/structured-streaming-programming-guide.html#quick-example they use netcat in the tutorial. When you launch nc ... is it “always on”?

2021-01-14T20:47:19.071300Z

(defn add-key
  [m]
  (assoc m :c 3))

(-&gt;&gt; [{:a 1} {:b 2}] ; &lt;- we're starting with a collection
     (filter #(contains? % :a)) ; the vector is placed last in `filter`
     first
     add-key ; &lt;- `assoc` wouldn't work directly because it expects the map as the first argument
     )
;; =&gt; {:a 1, :c 3}

2021-01-14T20:47:39.071500Z

My guess is this is written with spark as the client, and your code as the server to deal with exactly this kind of thing

2021-01-14T20:48:32.071800Z

Or not, I dunno

Santiago 2021-01-14T20:48:38.072Z

right, so I need to a server running plus the rabbitmq subscriber which also runs in a loop 🤷

2021-01-14T20:49:29.072300Z

My comment about networking hiccups is mostly on the scale of weeks to months, if your whole whatever has a shorter runtime than that you might not care

2021-01-14T20:54:13.072500Z

so, applying that strategy to your filter-image-url function:

(defn filter-image-url [imagedata]
  (let [just-width-and-height (fn [image] (select-keys image [:width :height]))]
    (-&gt;&gt; imagedata
         :levels
         (sort-by :name)
         (last)
         just-width-and-height)))

roelof 2021-01-14T21:16:43.073100Z

Can I use the same "trick" to get also the url ?

2021-01-14T21:32:55.073300Z

i guess it depends on the context and the shape of the data. you originally had a thread-last form that returned a single map with the :tiles value. you could add a function lift-first-tile to the thread last pipeline. that could be one approach

2021-01-14T21:34:35.073500Z

this is unrelated, but consider whether you really need to exclude the other keys. often in clojure folks find that it's useful, or at least harmless, to pass maps around that include additional data, even if it isn't strictly required.

2021-01-14T22:37:09.073900Z

is there an easy way to remove all existing taps

2021-01-14T22:37:51.074700Z

i find that i've polluted my set of taps with various functions over time and can't seem to remove-tap all of them

2021-01-14T22:38:10.075100Z

i should just restart the repl.

2021-01-14T22:40:18.075600Z

yeah, a big gotcha is adding a function itself as a tap, rather than the var holding the function

2021-01-14T22:40:32.076Z

which means when you redef the function, you no longer have the thing to call remove-tap on

dpsutton 2021-01-14T22:40:37.076200Z

(reset! @#'clojure.core/tapset #{}) should work

dpsutton 2021-01-14T22:41:41.076800Z

its just a private atom with a set. you can see from the source of remove-tap that there's no cleanup dance to do. just toss it

2021-01-14T22:50:11.077Z

thanks!

2021-01-14T22:53:04.077100Z

@noisesmith do you mean i should (add-tap (var f)) instead of (add-tap f)?

2021-01-14T23:04:50.077300Z

if you want to be able to reload f, yeah (or the equivalent of (var f), #'f)

2021-01-14T23:05:15.077500Z

f itself is no longer equal after you run defn again, the var remains equal to the var

2021-01-14T23:26:08.078400Z

I'm trying to use next/jdcb for a mssqlserver. I can't see how to specify "TrustedConnection=True;" rather than providing a username and password?

2021-01-14T23:29:19.078800Z

thanks, this has proven really useful just now in refining what i want f to be

seancorfield 2021-01-14T23:45:29.080200Z

@qmstuart You can pass extra properties via the db-spec hash map (that you build a datasource from): {:dbtype "mssql" :dbname "your-db" :TrustedConnection true :host "..."} something like that I expect.

seancorfield 2021-01-14T23:46:47.081500Z

Then call jdbc/get-datasource on that (and then use that return ds everywhere). If you're using a connection pool, you'll need to consult the docs for the pooling library in use and figure out how it passes properties to the driver manager.

seancorfield 2021-01-14T23:47:15.082Z

#sql is a good place for next.jdbc questions as well as general DB-related stuff.

2021-01-14T23:53:04.082400Z

thanks! I dunno how I didn't see :TrustedConnection. I looked at the docs, I swear 😄