beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
theSherwood 2020-11-21T00:38:07.141800Z

Hi. I’m having lein-cljsbuild fail to compile anything and my ide is showing linting errors in the cljsbuild code. Has anybody here experienced something like that before? I’m also not getting much of a stack trace:

Nov 20, 2020 5:34:26 PM com.google.javascript.jscomp.LoggerErrorManager println
SEVERE: /Users/path/target/cljsbuild-compiler-0/cljs/core.js:3359: ERROR - Parse error. primary expression expected
case ##Inf:
      ^

Nov 20, 2020 5:34:26 PM com.google.javascript.jscomp.LoggerErrorManager printSummary
WARNING: 1 error(s), 0 warning(s)
ERROR: JSC_PARSE_ERROR. Parse error. primary expression expected at /Users/path/sine/target/cljsbuild-compiler-0/cljs/core.js line 3359 : 6

theSherwood 2020-11-21T00:42:32.143200Z

I’m seeing the modules successfully compiled to js in the target directory. But they aren’t getting bundled. I’m totally thrown by this. Any help would be very appreciated.

st3fan 2020-11-21T00:57:03.143400Z

I went from this

(def default-settings
  {:enabled false
   :comment nil})

(def bot
  {:name "lock-pull-request"
   :private-key (slurp (io/resource "devbots/bots/lock-pull-request.key"))
   :id 21060
   :default-settings default-settings})
To this:
(defbot lock-pull-request 21060 :default-settings {:comment nil})

st3fan 2020-11-21T00:57:12.143700Z

Baby steps with macros

2020-11-21T01:39:20.144900Z

Custom def forms are almost always a bad idea

2020-11-21T01:40:27.146300Z

It moves you away from computing with values

2020-11-21T01:41:19.147300Z

5 is a value, it may have many names, it may be used directly without binding it to a name, and you'll never know

2020-11-21T01:42:57.148100Z

Make anonymous things, use the langues binding constructs to bind them to names

2020-11-21T01:49:01.151100Z

How come I am still getting a lazy sequence from this. How can I make it return a just a hash map? Shouldn’t doall force the sequence to be realized?

(->> [{:a 1 :b 2} nil nil]
     (remove nil?)
     (map #(merge % {:c 3}))
     (doall)

;; ({:a 1, :b 2, :c 3})

2020-11-21T01:50:32.152Z

A realized lazy sequence is still a lazy sequence

2020-11-21T01:50:55.152600Z

If you want the first element of a sequence use first

🙌 1
2020-11-21T02:30:26.159900Z

I have been doing research on records, and I still don't understand when they are appropriate. Isn't a big thing in Clojure about avoiding concretions? I understand that records have a key use case, namely for polymorphism via protocols. But I think that can also be achieved via having some "type" key of in the map, then checking the key to call different functions, right? One downside of this is that the function becomes closed for extension (see: expression problem), but it avoids the need to create a type. I also understand that it is more performant. But if performance is not an issue, we should probably just use map, is my thoughts.

2020-11-21T02:33:16.160900Z

I think I just need to see some real life examples where records are used, to get a feel for how they can be used

seancorfield 2020-11-21T04:07:02.162600Z

Right, the preference is to use hash maps -- unless you need dispatch on just a single "thing" and you need performance, in which case a record can help.

seancorfield 2020-11-21T04:08:07.163Z

Records can also implement multiple protocols etc.

seancorfield 2020-11-21T04:09:44.163600Z

@thomastayac_slack This is a helpful flowchart regarding Clojure data type selection https://github.com/cemerick/clojure-type-selection-flowchart

2020-11-21T05:09:27.170400Z

Hey guys 👋, I'm new here, first time asking a question. I'd greatly appreciate if someone could help me out with the most idiomatic way to do this data transformation - everything I've come up with feels extremely verbose and hard to understand. What I want to do is: apply a map fn to the first element in a collection which satisfies a given predicate. An example:

;; given the following collection:
(def coll [{:type "foo" :message ""}
           {:type "bar" :message ""}
           {:type "bar" :message ""}])

;; and assuming a function signature:
(defn map-first [fn pred coll]
  ;; ???
  )

;; I'm looking for the following sexp:
(map-first
  #(assoc % :message "hello!")
  #(= (:type %) "bar")
  coll)

;; ... to eval to:
[{:type "foo" :message ""}
 {:type "bar" :message "hello!"}
 {:type "bar" :message ""}]
What I've tried so far: • manually writing map-first in terms of loop and recur (this felt like a bad idea) • considered trying to write map-first in terms of reduce, but didn't know where to start

2020-11-22T06:43:44.253300Z

Ah yes, that makes sense.

seancorfield 2020-11-21T05:29:09.172100Z

How about:

(->> coll
  (filter #(= "bar" (:type %)))
  (map #(assoc % :message "hello!")))
Or if you just want the first element on its own after that, just thread it into (first)

seancorfield 2020-11-21T05:30:52.174Z

That's idiomatic and not very verbose -- it says exactly what it does: find me elements that have type bar, then add the message "hello!" to those elements. And you could just add (first) if you only want the first matching element @abaayd01

seancorfield 2020-11-21T05:32:15.176400Z

Now, behind the scenes, because filter and map are lazy and work with chunked sequences, they may evaluate more elements of the sequence than strictly needed to get just the first one, but if you're starting with a known, specific vector of hash maps, that's not really a big deal (just wastes some CPU cycles).

2020-11-21T05:32:52.177Z

Thanks for the reply @seancorfield! This approach:

(->> coll
  (filter #(= "bar" (:type %)))
  (map #(assoc % :message "hello!")))
Looks like it'll filter out the first element {:type "foo"}, from the collection though, no?

seancorfield 2020-11-21T05:33:17.177500Z

filter returns all the elements of a sequence that satisfy the predicate.

seancorfield 2020-11-21T05:34:02.178100Z

Oh, I see what you're after now... OK, that wasn't clear from what I read...

seancorfield 2020-11-21T05:34:29.178700Z

Hmm, that's kind of a weird requirement. What's the background behind that problem?

2020-11-21T05:35:41.180300Z

I thought someone might ask that question - my reduction of the original problem did feel little off, let me explain further, give me a moment.

2020-11-21T05:36:09.180700Z

(defn map-first
  [f pred coll]
  (first (reduce
    (fn [[acc done?] e]
      (if (and (not done?) (pred e))
        [(conj acc (f e)) true]
        [(conj acc e) false]))
    [[]]
    coll)))

seancorfield 2020-11-21T05:36:28.181400Z

I mean, yeah, it's totally doable in several ways, but it's a bit off the beaten track...

seancorfield 2020-11-21T05:37:08.181500Z

That's at least missing a )

2020-11-21T05:38:02.181800Z

Typed this on my phone, so might be missing a few more ) lol, but should do the trick

seancorfield 2020-11-21T05:38:04.182Z

It also reverses the collection.

seancorfield 2020-11-21T05:38:19.182300Z

The init expr should be [[]] not []

2020-11-21T05:38:48.182500Z

Ah yes sorry forgot

seancorfield 2020-11-21T05:39:16.182800Z

And I think it's a bit wasteful to walk the entire data structure. A loop / recur could short-circuit on the first match.

2020-11-21T05:39:50.183Z

My understanding is he wants the full collection back, but the first entry to equal pred modified

2020-11-21T05:40:07.183200Z

So only faster way is mutable coll, or maybe with splitting vector

2020-11-21T05:40:29.183400Z

Don't think this reverses the coll ? You sure

seancorfield 2020-11-21T05:40:49.183600Z

Yes, I tried it in a REPL 😛

seancorfield 2020-11-21T05:41:26.183800Z

loop / recur could at least just return (into acc coll) when it's done applying the conditional function.

2020-11-21T05:41:45.184Z

thanks for the solution @didibus, I think I'll need to study that one a bit at the REPL :thumbsup:

seancorfield 2020-11-21T05:41:50.184200Z

well, (into (conj acc (f (first coll))) (rest coll)) I guess

seancorfield 2020-11-21T05:49:18.185100Z

Here's a solution using loop/`recur` that doesn't walk the entire collection:

user=> (defn map-first [f pred coll]
         (loop [acc [] coll coll]
           (if (seq coll)
             (if (pred (first coll))
               (into acc (cons (f (first coll)) (rest coll)))
               (recur (conj acc (first coll)) (rest coll)))
             acc)))
#'user/map-first
user=> (map-first #(assoc % :message "hello!") #(= "bar" (:type %)) coll)
[{:type "foo", :message ""} {:type "bar", :message "hello!"} {:type "bar", :message ""}]
user=> 

👀 1
seancorfield 2020-11-21T05:50:14.185800Z

That could probably be cleaned up a bit with if-let and destructuring...

seancorfield 2020-11-21T05:52:46.187200Z

Here's a cleaner version:

user=> (defn map-first [f pred coll]
         (loop [acc [] coll coll]
           (if-let [[head & tail] (seq coll)]
             (if (pred head)
               (into acc (cons (f head) tail))
               (recur (conj acc head) tail))
             acc)))
#'user/map-first
user=> (map-first #(assoc % :message "hello!") #(= "bar" (:type %)) coll)
[{:type "foo", :message ""} {:type "bar", :message "hello!"} {:type "bar", :message ""}]
user=> 

2020-11-21T05:54:03.188600Z

ahhhhk, I think I see what you've done there @seancorfield: • you put the elements you've already applied the predicate to in acc • the remainder goes in coll • once you hit an element for which the pred is true you smush everything back together using into , applying f to the current element along the way

2020-11-21T05:55:20.190100Z

thanks for that, I think I'm going to rethink the original problem however, and see if i can come into it from a different angle

seancorfield 2020-11-21T05:55:49.190600Z

Yup. That at least stops at the first matching element, although it still "pours" the rest of the collection into what's been built so far (but at least it doesn't apply the pred to anything else).

🙌 1
seancorfield 2020-11-21T05:58:02.192100Z

This sort of problem is always a bit ugly in Clojure because the transformation is essentially stateful: it only applies to the first matching element. Making it apply to all matching elements is easy. Returning just the first matching element, transformed, is also easy.

seancorfield 2020-11-21T06:02:34.192800Z

Here's an alternative, using split-with, but I haven't tested it on any edge cases:

user=> (defn map-first [f pred coll]
         (let [[prelude [matched & others]] (split-with (complement pred) coll)]
           (-> []
               (into prelude)
               (conj (f matched))
               (into others))))
#'user/map-first
user=> (map-first #(assoc % :message "hello!") #(= "bar" (:type %)) coll)
[{:type "foo", :message ""} {:type "bar", :message "hello!"} {:type "bar", :message ""}]
user=> 
@abaayd01

seancorfield 2020-11-21T06:03:53.193800Z

(it doesn't account for matched & others being an empty sequence so it will produce a strange result if no elements match)

seancorfield 2020-11-21T06:06:26.194200Z

(it will work correctly if the first or last element are the match tho')

seancorfield 2020-11-21T06:09:55.195900Z

Here's a version of the above that works when no elements match @abaayd01:

user=> (defn map-first [f pred coll]
         (let [[prelude [matched & others :as postlude]] (split-with (complement pred) coll)]
           (-> []
               (into prelude)
               (cond-> (seq postlude) (conj (f matched)))
               (into others))))
#'user/map-first
user=> (map-first #(assoc % :message "hello!") #(= "bar" (:type %)) coll)
[{:type "foo", :message ""} {:type "bar", :message "hello!"} {:type "bar", :message ""}]
user=> (map-first #(assoc % :message "hello!") #(= "quux" (:type %)) coll)
[{:type "foo", :message ""} {:type "bar", :message ""} {:type "bar", :message ""}]
user=> (map-first #(assoc % :message "hello!") #(= "quux" (:type %)) (conj coll{:type "quux"}))
[{:type "foo", :message ""} {:type "bar", :message ""} {:type "bar", :message ""} {:type "quux", :message "hello!"}]
user=> (map-first #(assoc % :message "hello!") #(= "foo" (:type %)) (conj coll{:type "quux"}))
[{:type "foo", :message "hello!"} {:type "bar", :message ""} {:type "bar", :message ""} {:type "quux"}]
user=> 

2020-11-21T06:13:33.197500Z

Hmm, I think I prefer this alternative @seancorfield I feel like the mechanism of splitting the coll and re-stitching it back together with f applied to the matched element is much clearer without the if 's and recur floating around.

2020-11-21T06:14:48.198200Z

I've not used cond-> before though, so I'll need to study this version a bit I think

2020-11-21T06:38:18.198300Z

Ya I thought about doing that, you can do it with reduce as well, just wrap it in a reduced to short circuit. But I wasn't confident on the phone for it, I also not sure it'll be that much faster. Since we're still O(n), it's just maybe a slightly more optimized variant.

2020-11-21T06:40:27.198700Z

I'm super confused why it would reverse it. Conj on vector should add the the end... Weird

2020-11-21T06:52:22.204Z

@abaayd01 Using into will still walk the entire collection though. I'm thinking, if you know you have a vector, the fastest way might be:

(defn map-first
 [f pred coll]
 (let [idx
       (reduce
        (fn [i e]
         (if (pred e)
          (reduced i)
          (inc i)))
        0
        coll)]
  (assoc coll idx (f (get coll idx)))))
(map-first
 #(assoc % :name "yay")
 #(= :foo (:type %))
 [{:type :bar :name ""}
  {:type :bar :name ""}
  {:type :foo :name ""}
  {:type :bar :name ""}
  {:type :foo :name ""}])
(map-first
 #(assoc % :name "yay")
 #(= :foo (:type %))
 [{:type :bar :name ""}
  {:type :bar :name ""}
  {:type :foo :name ""}
  {:type :bar :name ""}
  {:type :foo :name ""}])

2020-11-21T06:56:42.205800Z

ahk I see, @didibus , so here, you're first walking up till you hit the matched element to pull out the index - then you use assoc passing in the idx to update the element in place.

2020-11-21T06:57:56.207500Z

Also, I didn't realise you could use assoc like that! I assume it has constant time complexity in that instance?

2020-11-21T06:59:10.208700Z

There I fixed it

2020-11-21T06:59:49.209100Z

Ya, so this should be O(idx)

seancorfield 2020-11-21T06:59:50.209400Z

That's quite slick. What does it do if the collection doesn't contain a match tho'?

2020-11-21T07:00:34.209600Z

nice

2020-11-21T07:00:51.210200Z

😝, you're asking too much for what I can do on my phone haha

seancorfield 2020-11-21T07:01:15.211100Z

Answer: same as my initial split-with version -- it sticks a new entry on the end:

user=> (map-first #(assoc % :message "hello!") #(= "quux" (:type %)) coll)
[{:type "foo", :message ""} {:type "bar", :message ""} {:type "bar", :message ""} {:message "hello!"}]

2020-11-21T07:01:29.211600Z

I think if you made the whole thing an if-let and returned the original coll on else then it just return the coll unmodified

seancorfield 2020-11-21T07:02:10.212300Z

You'd have to check idx against (count coll) but, yeah, you'd need a conditional for the no-match case.

seancorfield 2020-11-21T07:03:59.213300Z

You had [] initially, not [[]], so acc would be nil and (conj nil :x) produces a list not a vector. I see you edited it to have [[]]

2020-11-21T07:04:58.214700Z

Ideally though, if you care about performance and you're going to do this on large data. You probably want some kind of indexed structure, like a dataframe.

2020-11-21T07:06:42.215800Z

It'll treat your structure more like a database table. And you can have indexes on the columns. And then you can run queries on it and all which leverages the index.

seancorfield 2020-11-21T07:07:36.216600Z

(that strays from "beginners" tho'... which sort of goes back to me saying earlier that this is a problem that is a bit off the beaten track)

2020-11-21T07:10:54.216700Z

Agree, it's a use case I haven't seen often.

Lyderic Dutillieux 2020-11-21T15:16:33.227200Z

Hey guys, I'm trying to use prefix when requiring my libs within a file with (ns my.ns (:require ...)). Let's say I want to require [vendor1.library1 :as lib1] and [vendor1.library2 :as lib2] What is the correct syntax to prefix vendor1 ?

(:require
   [vendor1
     [library1 :as lib1]
     [library2 :as lib2]])
This one just above doesn't seem to work. The doc seems to only give the syntax for the (require ...) function but I couldn't find any hint for the (ns _ (:require ...)) macro. Any idea ?

2020-11-21T15:27:10.229800Z

If I wanted to dissoc :a in here and keep the type of the lazy sequence, how should I return the result?

(->> [{:a 1 :b 2} nil nil]
     (remove nil?)
     (map #(merge % {:c 3}))

;;=> ({:a 1 :b 2 :c 3})

2020-11-21T15:29:36.229900Z

I think you already had it

(:require
  [vendor1.library1 :as lib1]
  [vendor1.library2 :as lib2]

Lyderic Dutillieux 2020-11-21T15:31:06.230100Z

Actually, I require more than 2 libraries, so I want to use prefixing, just like here https://clojure.org/reference/libs#_prefix_lists

Lyderic Dutillieux 2020-11-21T15:34:31.230300Z

Maybe you can try this : https://whynotsoftware.github.io/wnaf-dissoc-in/

2020-11-21T15:54:11.230600Z

The example you show has a result that is a sequence containing one map. Do you want to handle a situation where the sequence can contain multiple maps, and you want to dissoc key :a in all of those maps?

2020-11-21T15:54:34.230800Z

If yes, then you can add (map #(dissoc % :a)) as another step in your ->> expression, at the end.

2020-11-21T15:55:45.231Z

You could also change your last step from the current (map #(merge % {:c 3})) to (map #(dissoc (merge % {:c 3}) :a))

2020-11-21T16:02:40.231200Z

The syntax you are using is correct. Why you think it is not working?

Lyderic Dutillieux 2020-11-21T17:17:20.231400Z

I'm using it in cljs with shadow-cljs. I guess this is an issue with the code analyser of shadow-cljs

Lyderic Dutillieux 2020-11-21T17:17:40.231600Z

Thanks for the feedback

dpsutton 2020-11-21T17:43:09.232200Z

Cljs does not support the prefix style for requiring

👍 1
Lyderic Dutillieux 2020-11-21T17:57:01.232500Z

I didn't know, thanks 😉

hequ 2020-11-21T19:11:51.238800Z

Am I missing something about repl use here? If I have dev-server.clj which runs pedestal rest api with jetty and then that file refers to main.clj and eventually that refers to routes.clj . If I do changes to routes.clj and then evaluate dev-server.clj , shouldn’t that also evaluate all those transient dependencies as well? I’m using vscode with calva running nrepl (clj). Now I have to evaluate all these files to see those changes actually running in repl. Is it working now as intended or do I have some configuration issue somewhere?

william 2020-11-21T19:12:10.239100Z

If I do lein new figwheel hello_world , and then cd hello_world &amp;&amp; lein figwheel, the repl won't connect to my browser, and I get the error message Loading failed for the &lt;script&gt; with source "<http://0.0.0.0:3449/js/compiled/hello_world.js>"

william 2020-11-21T19:35:22.239700Z

in fact, no hello_world.js is generated in that folder. Maybe I need to run something the first time?

2020-11-21T20:17:21.239800Z

Yes it should return one map and I am going to run checks on it.`(dissoc (merge % {:c 3}) :a))` should work. Thanks for the replies!~

hequ 2020-11-21T20:50:19.240Z

apparently there is a library for this: tools.namespace

st3fan 2020-11-21T21:03:17.240600Z

Is it possible to discover the version from project.clj at runtime?

seancorfield 2020-11-21T21:12:57.241300Z

@st3fan In general, no, because project.clj isn't going to be available to an application running in an uberjar.

seancorfield 2020-11-21T21:14:42.243Z

What people tend to do is have the version in a file that is part of the code (or a resource that is read in) and have escaped code in project.clj to read it into the defproject form (because Leiningen will execute ~-escaped forms in project.clj)

st3fan 2020-11-21T21:15:37.243300Z

cool that is a good hint

st3fan 2020-11-21T21:15:59.243800Z

i usually have a /version endpoint in my APIs so that it is easy to find out the project versin, git tag, etc.

seancorfield 2020-11-21T21:49:54.244400Z

We have a version.txt file in /resources in our main app at work, and we read it in at startup.

seancorfield 2020-11-21T21:51:41.244900Z

(we also have a /version endpoint on it, to return the current API version)

seancorfield 2020-11-21T21:51:48.245100Z

^ @st3fan

seancorfield 2020-11-21T21:54:01.246100Z

We also have an endpoint on every app that lists the git tag, Clojure version, and other "interesting" stuff that we might want from a monitoring p.o.v.

seancorfield 2020-11-21T21:54:34.246700Z

(we're running Clojure 1.10.2-alpha4 in production according to the probe URL 🙂 )

st3fan 2020-11-21T23:13:12.246900Z

🙂