This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-02-22
Channels
- # announcements (10)
- # babashka (40)
- # beginners (192)
- # calva (9)
- # cider (2)
- # clj-kondo (9)
- # clojure (69)
- # clojure-dev (15)
- # clojure-europe (29)
- # clojure-gamedev (6)
- # clojure-italy (2)
- # clojure-nl (41)
- # clojure-spec (49)
- # clojure-uk (11)
- # clojurescript (68)
- # conjure (1)
- # cryogen (20)
- # cursive (37)
- # data-oriented-programming (10)
- # data-science (4)
- # datahike (7)
- # datomic (8)
- # depstar (14)
- # emacs (7)
- # events (2)
- # figwheel-main (1)
- # fulcro (81)
- # honeysql (22)
- # hugsql (5)
- # juxt (3)
- # leiningen (8)
- # lsp (314)
- # malli (20)
- # meander (15)
- # membrane (20)
- # mid-cities-meetup (11)
- # practicalli (2)
- # reagent (2)
- # remote-jobs (2)
- # ring-swagger (1)
- # rum (3)
- # sci (21)
- # shadow-cljs (52)
- # startup-in-a-month (1)
- # testing (9)
- # tools-deps (41)
- # vim (8)
- # xtdb (4)
Is there a difference between update and assoc function? It seems to me they both have the same result.
i use (doc f) while in repl
Perfect yeah. So (doc assoc) should say it takes a key and a value. Doc update should say it takes a key and a function
i see. thanks. if you use a function in assoc as in (assoc [] 0 #(str \a)), the value will be the function itself and not the result of executing that function. in update, it will replace the current value with the result of executing the function.
@cjpangilinan There's a subtlety there: #(str \a)
is a function of no arguments that returns "a"
but update
requires a function of one or more arguments:
user=> (assoc [] 0 #(str \a))
[#object[user$eval1$fn__2 0x74a9c4b0 "user$eval1$fn__2@74a9c4b0"]]
user=> (update [] 0 #(str \a))
Execution error (ArityException) at user/eval5 (REPL:1).
Wrong number of args (1) passed to: user/eval5/fn--6
user=> (update [] 0 (fn [_] (str \a)))
["a"]
user=>
The (fn [_] (str \a))
function accepts one argument and ignores it -- _
is idiomatic naming in Clojure for an argument that a function does not use but otherwise has no special meaning.
Yes that is interesting. I just tried out ‘(def -str #(str %))’ and ‘(update [] 0 #(-str \a))’ and works.. ofc that isnt good idea
@U2T2ZEVPC I don't think you tried exactly that -- it does not work, for exactly the same reason as I observed: #(-str \a)
is still a function of no arguments and is not compatible with update
:
user=> (def -str #(str %))
#'user/-str
user=> (update [] 0 #(-str \a))
Execution error (ArityException) at user/eval151 (REPL:1).
Wrong number of args (1) passed to: user/eval151/fn--152
user=>
(but Clojure on the JVM should be considered the "reference")
I would consider it a bug in cljs to allow that invocation, but I suspect it's because JS itself doesn't check function arities strictly?
It should also be noted that cljs doesn't have the character data type like Clojure does: in Clojure \a
is a single character, in cljs \a
is just a single character string "a"
.
(! 733)-> clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "RELEASE"}}}' -M -m cljs.main
ClojureScript 1.10.773
cljs.user=> \a
"a"
cljs.user=> "a"
"a"
cljs.user=> (= \a "a")
true
cljs.user=> ^D
Sun Feb 21 22:18:35
(sean)-(jobs:0)-(~/clojure)
(! 734)-> clj
Clojure 1.10.2
user=> \a
\a
user=> "a"
"a"
user=> (= \a "a")
false
user=>
Unfortunately, there are lots of small differences between Clojure and cljs (but nowhere near as many now as there used to be!).
oh yeah. i got the same error about arity. but i change it to #(str %).
@cjpangilinan #(str %)
is exactly the same as just str
on its own. Well, not quite exactly, since str
is variadic -- but both str
and #(str %)
can be called with a single argument.
user=> (update [] 0 #(str %))
[""]
user=> (update [] 0 str)
[""]
user=>
Hi guys, I just got set up running Calva and Clover in VS Code, just wondering about this workspace concept in VS Code, do I need to bother with that at all or can I just open my Clojure project folder and get cracking?
@flyingpython If you're just working with one project -- one folder -- then you can pretty much ignore the workspace stuff.
My main use for it is to have a workspace for "work" (with two main projects open) and a workspace for "open source" (with half a dozen active GitHub projects in it). With the former I use Jira and BitBucket, with the latter I use GitHub. The nice thing about the "workspace" idea is that I can switch contexts very easily and VS Code remembers all the files I had open the last time I was using each workspace.
Ah OK thank you very much for the info Sean 😉
Hey guys, it might be a noob question! I want to know the difference between defn
& defn-
also suggest where to use, where not to use!
Thanks!
https://clojuredocs.org/clojure.core/defn- public or non-public function, check the example
thanks
a lot of experienced Clojurists don't make too much use of this. You can also hide things in an internal
or impl
namespace if you really don't want people calling internal functions
defn-
is the same as defn
, except that the function defined is private to the namespace, the same as if you had written (defn ^:private foo [x] ...)
Such functions cannot be called from other namespaces, without a special trick. Some Clojure developers like to use private functions to indicate that callers should not call that function as part of the public API of your code. Others prefer to do that via documentation instead, and/or to create a namespace specifically for all public API functions, and leave implementation detail functions for separate namespaces.
thanks for the info @andy.fingerhut
Using core.async channels. Do you know if there are any limitations on the data size per message?
Is that possible to create faster transducers? I compared few versions, but Im not sure I understand fully the topic. Maybe I did these versions in wrong way https://gist.github.com/damesek/e7023c0bc434a42952128b2f33c702dc? somebody could advise how could I create to create a faster/ better transducer? Thanks
Your (map inc data) benchmark is not doing the work due to laziness (wrap a doall around that or something)
Same for r/map I think - you have to reduce the reducible collection to actually do the work
My point is that these transducers are fast, you’re just not getting the correct slow times for sequences
Hello! Can anyone help me find a implementation for a function that should replace a text with a new text at specific range?
(update-text "some \ncool\n\n text" "boring" {:start {:line 2
:column 1}
:end {:line 2
:column 4}})
=> "some \nboring\n\n text"
The issue seems to be the line/col thing.
Not sure it's a beginner question but I could not figure it out how to achive that and I thought this channel could help 😅I don't have a function handy to give you that I know already does this, but if you can take a string s
, and any number of {:line <x> :column <y>}
maps, and calculate for each one the 0-based index in s
where that line and column is, and those indices are start-idx
and end-idx
, then I think all you need to do after that is (str (subs s 0 start-idx) replacement-string (subs s end-idx))
(perhaps with some off-by-one mistakes there).
Thank you for the response!
Hum, I think I got your point, it makes sense, I just need to find the start-idx
/ end-idx
like you said. Probably can get that with some for loops
Sorry, I gave you the answer for the easy part, not the harder part 🙂
@ericdallo what about something like:
String myName = "domanokz";
String newName = myName.substring(0,4)+'x'+myName.substring(5);
but then in clojure?but what about changes that add more lines? for example
(update-text "some \ncool\n text" "boring\n text" {:start {:line 2
:column 1}
:end {:line 3
:column 6}})
working on htis one
are lines indexed by 1?
what if the middle contains more than one line?
how is the replacement done then?
I don't get it, it should behave like a block of code change, you remove a block of code and replace by another
ahh okay i see
so maybe an algorithm: cut out the original delta and then concat the new input in between
yes, that was @andy.fingerhut suggestion, I'm trying to find the start and end idx so that would work
(require '[clojure.string :as str])
(defn offset [lines line col]
(loop [lines (seq lines)
buf (java.lang.StringBuffer.)
idx 0]
(if lines
(if (= line idx)
(+ (count (str buf)) col line)
(recur (next lines)
(.append buf (first lines))
(inc idx)))
offset)))
(def string "foobar\nbar\nbaz")
(def os (offset (str/split-lines "foobar\nbar\nbaz") 1 1))
(prn (.charAt string os)) ;;=> \a
Then you can use these offsets, to split the original string into three parts and create the new one from the first and last part + the new one in the middle
You can optimize this by searching for the next end line and end col by just continuing the loop
You should take into account Windows newline delimiters, which can be two characters
Yes, this looks a alternative indeed to https://clojurians.slack.com/archives/C053AK3F9/p1614006073168100
@ericdallo try this:
(defn update-text [text
update-text
{{start-line :line start-column :column} :start
{end-line :line end-column :column} :end}]
(let [[prefix middle suffix]
(->> (clojure.string/split-lines text)
(map-indexed #(vector %1 %2))
(partition-by #(or (= (dec start-line) (first %))
(= (dec end-line) (first %))))
(map #(map second %)))
middle-pre (subs (first middle) 0 (dec start-column))
middle-suff (subs (last middle) end-column)
prefix (clojure.string/join "\n" prefix)
suffix (clojure.string/join "\n" suffix)]
(-> [prefix middle-pre update-text middle-suff suffix]
(clojure.string/join))))
(update-text "some \ncool\n text" "boring\n text"
{:start {:line 2 :column 1} :end {:line 3 :column 4}})
It might have an off-by-one error on the columns, but I'm pretty sure it works
partition-by
is really the key function here that does all the hard work
yes there is an error*
let me fix
(defn update-text [text
update-text
{{start-line :line start-column :column} :start
{end-line :line end-column :column} :end}]
(let [[prefix middle suffix]
(->> (clojure.string/split-lines text)
(map-indexed #(vector %1 %2))
(partition-by #(and (<= (dec start-line) (first %))
(>= (dec end-line) (first %))))
(map #(map second %)))
middle-pre (subs (first middle) 0 (dec start-column))
middle-suff (subs (last middle) end-column)
prefix (clojure.string/join "\n" prefix)
suffix (clojure.string/join "\n" suffix)]
(-> [prefix middle-pre update-text middle-suff suffix]
(clojure.string/join))))
(update-text "some \ncool\n text" "boring\n text"
{:start {:line 2 :column 1} :end {:line 3 :column 4}})
yup just wanted to illustrate
Thank you! I'll test it with some weird inputs and check if it's working, thank you very much for the help 😄
(update-text "some \ncool\n\n text" "boring" {:start {:line 2
:column 1}
:end {:line 2
:column 4}})
=> "some boring\n text"
remove (dec start-column)
and replace with start-column
wait what was wrong with the dec?
yeah so stick a newline in between prefix and middle-pre
go back to dec
[prefix "\n" middle-pre update-text middle-suff "\n" suffix]
sorry i closed my repl
will try multiple inputs, thanks @U01G47XUNHM ❤️
no problem!
(require '[clojure.string :as str])
(defn offset [lines line col]
(loop [lines (seq lines)
buf (java.lang.StringBuffer.)
idx 0]
(if lines
(if (= line idx)
(+ (count (str buf)) col line)
(recur (next lines)
(.append buf (first lines))
(inc idx)))
offset)))
(def string "xxxxxxx\n123\n123")
(defn replace-str [original line col end-line end-col replacement]
(let [lines (str/split-lines string)
start-offset (offset lines line col)
end-offset (offset lines end-line end-col)
[prefix suffix] [(subs original 0 start-offset)
(subs original end-offset (count original))]]
(str/join [prefix replacement suffix])))
(prn (replace-str string 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"
You can optimize offset by collapsing the finding of the first and second offset into one loop
@ericdallo I have similar code in the clj-kondo LSP server ;) https://github.com/clj-kondo/clj-kondo.lsp/blob/d9d29a5d4a7955daef0f5c196a2054f9c75b7609/server/src/clj_kondo/lsp_server/impl/server.clj#L137
@U04V15CAJ nice solution
definitely more production than mine, i'm fond of code golfing myself
with similar I mean, it doesn't do the incremental stuff, but I had to fix some stuff related to how it shows diagnostics which also required me to split the lines, etc
Nice, it looks easier to understand indeed, I wonder about the performance, if it's relevant
I would say anything that is executed on each keystroke should be as performant as possible
if you're really after performance you should just write a loop with StringBuffer
I could have sworn I had StringBuffer available in babashka, but that is java.lang.StringBuilder I see now...
@U01G47XUNHM what about texts that don't contain new lines?
(update-text "(+ 1 1)" "2" {:start {:line 1 :column 6}
:end {:line 1 :column 7}})
@ericdallo My version returns:
(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" ))
;;=> "(+ 1 2)"
Yeah it works perfectly @U04V15CAJ
@ericdallo on Windows this line (+ (count (str buf)) col line)
should probably by incremented by one, if the newline is represented by 2 characters
wait, windows has URI issues related to double \
and etc, why this is related with replacing a text?
because we are splitting on new line and then trying to find the offset by calculating the length of each line + one for the newline
But your script is already using string/split-lines
that take cares of that, right?
Here is the more optimized version:
(require '[clojure.string :as str])
(defn offsets [lines line col end-line end-col]
(loop [lines (seq lines)
buf (java.lang.StringBuilder.)
idx 0]
(when lines
(if (= line idx)
[(+ (count (str buf)) col line)
(loop [lines lines
buf buf
idx idx]
(when lines
(if (= end-line idx)
(+ (count (str buf)) end-col end-line)
(recur (next lines)
(.append buf (first lines))
(inc idx)))))]
(recur (next lines)
(.append buf (first lines))
(inc idx))))))
(defn replace-str [original line col end-line end-col replacement]
(let [lines (str/split-lines original)
[start-offset end-offset] (offsets lines line col end-line end-col)
[prefix suffix] [(subs original 0 start-offset)
(subs original end-offset (count original))]]
(str/join [prefix replacement suffix])))
(prn (replace-str "xxxxxxx\n123\n123" 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"
(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" )) ;;=> "(+ 1 2)"
Using that works too 🙂 Thank you @U04V15CAJ will follow with that
@ericdallo 2 improvements: - don't use StringBuilder, it isn't necessary - don't use OS specific newline delimiter
(require '[clojure.string :as str])
(defn offsets [lines line col end-line end-col]
(loop [lines (seq lines)
offset 0
idx 0]
(when lines
(if (= line idx)
[(+ offset col line)
(loop [lines lines
offset offset
idx idx]
(when lines
(if (= end-line idx)
(+ offset end-col end-line)
(recur (next lines)
(+ offset (count (first lines)))
(inc idx)))))]
(recur (next lines)
(+ offset (count (first lines)))
(inc idx))))))
(defn replace-str [original line col end-line end-col replacement]
(let [lines (str/split original #"\n") ;; don't use OS specific line delimiter!
[start-offset end-offset] (offsets lines line col end-line end-col)
[prefix suffix] [(subs original 0 start-offset)
(subs original end-offset (count original))]]
(str/join [prefix replacement suffix])))
(prn (replace-str "xxxxxxx\n123\n123" 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"
(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" )) ;;=> "(+ 1 2)"
@U04V15CAJ For some reason your function is failling for some cases, I could only reproduce with that test:
(is (= "(+ 1 1)\n" (#'f.file-management/replace-text "(+ 1 1)\n" "\n" 1 0 1 0)))
It gives a NPE
Not sure it's the only issue though(require '[clojure.string :as str])
(defn offsets [lines line col end-line end-col]
(loop [lines (seq lines)
offset 0
idx 0]
(if (or (not lines)
(= line idx))
[(+ offset col line)
(loop [lines lines
offset offset
idx idx]
(if (or (not lines)
(= end-line idx))
(+ offset end-col end-line)
(recur (next lines)
(+ offset (count (first lines)))
(inc idx))))]
(recur (next lines)
(+ offset (count (first lines)))
(inc idx)))))
(defn replace-str [original line col end-line end-col replacement]
(let [lines (str/split original #"\n") ;; don't use OS specific line delimiter!
[start-offset end-offset] (offsets lines line col end-line end-col)
[prefix suffix] [(subs original 0 start-offset)
(subs original end-offset (count original))]]
(str/join [prefix replacement suffix])))
(prn (replace-str "xxxxxxx\n123\n123" 1 1 2 1 "dude")) ;;=> "xxxxxxx\n1dude23"
(prn (replace-str "(+ 1 1)" 0 5 0 6 "2" )) ;;=> "(+ 1 2)"
(prn (replace-str "(+ 1 1)\n" 1 0 1 0 "\n")) ;;=> "(+ 1 1)\n\n"
Also notes that this now works:
(prn (replace-str "(+ 1 1)\n" 0 3 0 3 "1 ")) ;;=> "(+ 1 1 1)\n"
Updated version
@ericdallo you maintain emacs-LSP?
That's awesome tooling I thank you for that
that's high level, i don't code too much anymore
Hello everyone
I’m creating a RESTful API by using metosin/compojure-api
(specifically compojure.api.sweet/api
), but I have a little problem. I want to censor some keys in every response.
I added a middleware by using the extra middleware vector (`:middleware` key), but it is not applied on the default exceptions handlers. I saw the code of [`compojure.api.middleware/api-middleware`](https://github.com/metosin/compojure-api/blob/5c88f32fe56cdb6fcdb3cc506bb956943fcd8c17/src/compojure/api/middleware.clj#L213) and I found that middlewares are wrapped by these exceptions handlers.
Could you give me a suggestion of how can I define a middleware that applies on every response? I tried to apply it after api
, but it returns an InputStream
on the body.
Thanks!
I have a let
that includes an api call. What’s the ‘right’ way to bind in a let that doesn’t mess up if one of the args (the api call response) is nil?
(let [inside-temp (-> (read-reg bus reg-temp)
(get 1)
bytes->val
reading->C)
outside-temp (get-manhattan-temp) ; can be nil
now (java.time.LocalDateTime/now)
min-temp (minimum-legal-temp now outside-temp)]
I could do a when
and then another let
to bind min-temp
but that seems ugly
@grazfather You can do min-temp (when outside-temp (minimum-legal-temp ...))
ok cool, and is when outside-temp
better thn when (not (nil? outside-temp
?
assuming that outside-temp
is a number or nil, you can just use (when outside-temp ..)
it might nice to short-circuit the whole thing with when-let
on get-manhattan-temp
so you don't do any work that's not necessary
ah, I did not know about when-let
what did i do wrong here? Can someone help? I’m expecting ["1" "2"]
but only getting an empty array
(let [ids []]
(doseq [jdoc ({:id "1" :data "x"} {:id "2" :data "y"})]
(conj ids (:id jdoc)))
ids)
@UJ5N28CL8 that happens because (conj ids (:id jdoc))
is just returning the new seq, but never updating in place your ids
binding.
you're probably looking for something along the line of:
(map :id [{:id "1" :data "x"} {:id "2" :data "y"}])
@grazfather I think you should take a look at if-let
but if that doesn't satisfy your use case, I recommend taking a look at the following macro: https://github.com/schmidt73/guidescan-web/blob/master/src/guidescan_web/utils.clj
I ended up using a very similar when-let*
if-let*
binds forms but short circuits to the else branch if any of the bindings are nil.
It's very useful in general and I think there was some debate whether it should be included in the clojure.core
In your case you would simply replace your let
with if-let*