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
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.
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})
Baby steps with macros
Custom def forms are almost always a bad idea
It moves you away from computing with values
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
Make anonymous things, use the langues binding constructs to bind them to names
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})
A realized lazy sequence is still a lazy sequence
If you want the first element of a sequence use first
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.
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
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.
Records can also implement multiple protocols etc.
@thomastayac_slack This is a helpful flowchart regarding Clojure data type selection https://github.com/cemerick/clojure-type-selection-flowchart
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 startAh yes, that makes sense.
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)
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
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).
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?filter
returns all the elements of a sequence that satisfy the predicate.
Oh, I see what you're after now... OK, that wasn't clear from what I read...
Hmm, that's kind of a weird requirement. What's the background behind that problem?
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.
(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)))
I mean, yeah, it's totally doable in several ways, but it's a bit off the beaten track...
That's at least missing a )
Typed this on my phone, so might be missing a few more ) lol, but should do the trick
It also reverses the collection.
The init expr should be [[]]
not []
Ah yes sorry forgot
And I think it's a bit wasteful to walk the entire data structure. A loop
/ recur
could short-circuit on the first match.
My understanding is he wants the full collection back, but the first entry to equal pred modified
So only faster way is mutable coll, or maybe with splitting vector
Don't think this reverses the coll ? You sure
Yes, I tried it in a REPL 😛
loop
/ recur
could at least just return (into acc coll)
when it's done applying the conditional function.
thanks for the solution @didibus, I think I'll need to study that one a bit at the REPL :thumbsup:
well, (into (conj acc (f (first coll))) (rest coll))
I guess
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=>
That could probably be cleaned up a bit with if-let
and destructuring...
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=>
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
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
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).
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.
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(it doesn't account for matched & others
being an empty sequence so it will produce a strange result if no elements match)
(it will work correctly if the first or last element are the match tho')
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=>
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.
I've not used cond->
before though, so I'll need to study this version a bit I think
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.
I'm super confused why it would reverse it. Conj on vector should add the the end... Weird
@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 ""}])
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.
Also, I didn't realise you could use assoc
like that! I assume it has constant time complexity in that instance?
There I fixed it
Ya, so this should be O(idx)
That's quite slick. What does it do if the collection doesn't contain a match tho'?
nice
😝, you're asking too much for what I can do on my phone haha
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!"}]
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
You'd have to check idx
against (count coll)
but, yeah, you'd need a conditional for the no-match case.
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 [[]]
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.
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.
(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)
Agree, it's a use case I haven't seen often.
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 ?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})
I think you already had it
(:require
[vendor1.library1 :as lib1]
[vendor1.library2 :as lib2]
Actually, I require more than 2 libraries, so I want to use prefixing, just like here https://clojure.org/reference/libs#_prefix_lists
Maybe you can try this : https://whynotsoftware.github.io/wnaf-dissoc-in/
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?
If yes, then you can add (map #(dissoc % :a))
as another step in your ->>
expression, at the end.
You could also change your last step from the current (map #(merge % {:c 3}))
to (map #(dissoc (merge % {:c 3}) :a))
The syntax you are using is correct. Why you think it is not working?
I'm using it in cljs with shadow-cljs. I guess this is an issue with the code analyser of shadow-cljs
Thanks for the feedback
Cljs does not support the prefix style for requiring
I didn't know, thanks 😉
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?
If I do lein new figwheel hello_world
, and then cd hello_world && lein figwheel
, the repl won't connect to my browser, and I get the error message Loading failed for the <script> with source "<http://0.0.0.0:3449/js/compiled/hello_world.js>"
in fact, no hello_world.js
is generated in that folder. Maybe I need to run something the first time?
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!~
apparently there is a library for this: tools.namespace
Is it possible to discover the version from project.clj
at runtime?
@st3fan In general, no, because project.clj
isn't going to be available to an application running in an uberjar.
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
)
cool that is a good hint
i usually have a /version
endpoint in my APIs so that it is easy to find out the project versin, git tag, etc.
We have a version.txt
file in /resources
in our main app at work, and we read it in at startup.
(we also have a /version
endpoint on it, to return the current API version)
^ @st3fan
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.
(we're running Clojure 1.10.2-alpha4 in production according to the probe URL 🙂 )
🙂