This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-27
Channels
- # announcements (3)
- # babashka (16)
- # beginners (177)
- # calva (102)
- # cider (2)
- # clj-kondo (12)
- # clojars (10)
- # clojure (91)
- # clojure-argentina (3)
- # clojure-australia (5)
- # clojure-europe (16)
- # clojure-nl (1)
- # clojure-uk (10)
- # clojurescript (73)
- # community-development (8)
- # cursive (9)
- # depstar (7)
- # fulcro (5)
- # helix (1)
- # introduce-yourself (1)
- # jobs-discuss (18)
- # lsp (32)
- # luminus (1)
- # malli (2)
- # music (4)
- # off-topic (20)
- # pathom (19)
- # polylith (15)
- # re-frame (4)
- # reagent (6)
- # ring (13)
- # sci (36)
- # shadow-cljs (7)
- # spacemacs (4)
- # sql (3)
- # tools-deps (112)
- # vim (21)
I have an essay I want to write about aesthetics/beauty of Clojure/Lisp and wanted to ask if there are any resources I might want to look at. Was not really sure which channel to post it in Slack. So I decided to post it as a clojureverse question instead - https://clojureverse.org/t/aesthetics-of-clojure-lisp-resources-to-look-at/7932
(and an article about the video, I haven’t read it though: https://www.lvguowei.me/post/the-most-beautiful-program-ever-written/)
I posted a link to Gene Kim's "Love Letter to Clojure" on ClojureVerse
Hi I have been having a problem with configuring the clojure project. Anyone who help me would be appreciated. Thanks!
You'll need to explain in more detail what you mean by that, and what you've tried and what errors you got and/or what unexpected behavior you got.
I’m slowly becoming comfortable with recursion again after having spent years in imperative C-Style languages (and still).
However I suspect I’m reaching for recur
too quickly now. How would a experienced Clojurian write this function?
(defn first-distinct
[c1 c2]
(loop [c1 c1, c2 c2
i 0]
(let [x1 (first c1)
x2 (first c2)]
(if (not= x1 x2)
[x1 x2 i]
(recur (rest c1) (rest c2)
(inc i))))))
Something like this
(defn first-distinct [c1 c2]
(->> (map vector c1 c2)
(map-indexed
(fn [i [x y]]
(when (not= x y)
[x y i])))
(filter some?)
first))
(first-distinct [0 1 2 3 4] [0 1 2 30 4]) ;; [3 30 3]
It’s rather annoying to write code that deals with indexes in clojure, it goes against “the functional way”. And it’s often because of people trying to translate non-functional code into clojure. If you don’t need the index, you could write the function like this:
(defn first-distinct-2 [c1 c2]
(->> (map vector c1 c2)
(filter (fn [[x y]] (not= x y)))
first))
(first-distinct-2 [0 1 2 3 4] [0 1 2 30 4]) ;; [3 30 3]
In general, if you find yourself using indexes in Clojure, it’s worth stopping and asking yourself if you really need to
Some benefits of writing code this way is that a lot of edge cases are handled kinda by default, for example:
(first-distinct [] []) ;; => nil
(first-distinct [1 2 3] []) ;; => nil
(first-distinct [1] [1]) ;; => nil
Without having to write terminating conditionOh I like the range
trick @U0178V2SLAY
I have to think more deeply about why I use an index here. I’m building something bottom up
I’ts not quite doing what I need though, because I want to know both distinct elements and where it occurs, which is why I return a tuple in my original function.
@U0178V2SLAY’s solution (as well as @U7S5E44DB 's original one) also give you the index, so either should be what you need
(defn first-distinct-2
[c1 c2]
(->> (map vector c1 c2 (range))
(drop-while (fn [[a b _]] (= a b)))
(first)))
(first-distinct-2 [:a :b] [:a :b :c]) => nil
I was just about to mention that my solution assumes the inputs c1 and c2 are of equal length 🙂 (because map
ignores any extra elements if the given collections are not of equal length)
you could use cycle on the shortest collection edit: actually scratch that, filling it with nils is probably better
(defn first-distinct-2
[c1 c2]
(let [len (max (count c1) (count c2))]
(->> (map vector (concat c1 (repeat nil)) (concat c2 (repeat nil)) (range len))
(drop-while (fn [[a b _]] (= a b)))
(first))))
... or make the range the shortest collection?I’m now getting the feeling that I’m trying to make things more complicated than they need to be and should just use recur 😄
Something to take away: its often useful to think of collections as infinite lazy sequences
Ah I like this! it solves the problem of having to determine if I encounter a nil
or the actual end/empty list
feels a bit like mini-parsing or something like that. I’ve seen this pattern of signifying something meaningful with keywords in other functions as well and I like this.
alternatively:
user=> (def a [1 2 3 4 9])
#'user/a
user=> (def b [1 2 3 5 8])
#'user/b
user=> (first (for [c (range) :let [x (get a c), y (get b c)] :when (not= x y)] [x y c]))
[4 5 3]
or
user=> (first (keep-indexed #(when (not= (first %2) (second %2)) [%2 %]) (partition 2 (interleave a b))))
[(4 5) 3]
(range)
is interesting! It is implemented in terms of (iterate inc' 0)
which is an object that holds the value of first
and possibly next
(cached) with the provided “seed” as 0
. whenever you call first
or next
you get the cached value and with rest
you get a new Iterate
with inc
invoked for next
as the “seed”. I think I’m starting to get how lazyness works. It is just a simple interface on a chain of objects that may or may not be cached and evaluate on demand.
Be sure to check out articles such as https://stuartsierra.com/2015/08/25/clojure-donts-lazy-effects when learning lazyness
my personal rule is not to use recur for code that walks a collection in order. reduce if I need side effects or to propagate state or early exit, map/filter/etc. otherwise
(defn first-distinct
[c1 c2]
(loop [c1 c1, c2 c2
i 0]
(let [x1 (first c1)
x2 (first c2)]
(if (and (empty? c2) (empty? c1))
nil
(if (not= x1 x2)
[x1 x2 i]
(recur (rest c1) (rest c2)
(inc i)))))))
Does the recently released tools.build also allow one to AOT compile certain namespaces, similar to lein’s :aot
?
Yes, using the compile-clj
task function. https://clojure.github.io/tools.build/clojure.tools.build.api.html#var-compile-clj
That makes sense, thank you for the info. It works really well, but is there some way I can get it to run my aot before spawning e.g a repl?
How are you trying to run the task?
Currently, I manually run clj -T:build aot
and then I simply cider-jack-in from Emacs; ideally, I’d somehow skip this manual step.
Ah you mean you want the AOT to happen before the jack-in happens?
At least before the jack-in is done and my repl connected 🙂
I'm certain that's possible but may require an Emacs config thingie which I'm pretty sketchy on myself.
perhaps I can make it work with tools.build + :deps/prep-lib
This seems to be the (open) challenge that I’m having as well: https://stackoverflow.com/questions/68388486/how-to-compile-all-source-and-tests-before-running-m-or-x-from-deps-edn
Shouldn't (#(%) "7s")
just return "7s"
? It's giving me java.lang.String cannot be cast to clojure.lang.IFn
and I don't understand why.
I only want a fn that returns whatever has been passed in.
identity
fits your needs. type '#(%)
at the repl to see the expansion of that form
Thanks!
Could someone explain what the Spec Guide means by a regex op? For example, s/keys*
is described as returning a regex op.
https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html#clojure.spec.alpha/keys*
They're like regular expressions but for sequences. Does that help?
Not really. I still don't understand how keys
differs from keys*
. It says the latter can be embedded inside a sequential regex structure. They both describe req/optional keys and validate all registered keys in the map. What is it that keys
cannot do that keys*
can?
Is this a good demo of the difference? I'm presuming this is meant by "embedded in a sequential regex structure":
(s/def ::player (s/keys* :req [::name ::score]))
(s/def ::match (s/tuple ::player ::player))
keys
is like {:keys [...]}
in a function argument list, i.e., a hash map. keys*
is like & {:keys [...]}
, i.e., named arguments.
Ah, so if I change the keys*
example in the guide to conform a map instead of a vector, I get:
[:my.config/id :s1 :my.config/host "" :my.config/port 5555] - failed: map? spec: :my.config/server
That makes sense now. Basically keys
is strictly for maps, while the star version is meant for sequential structures:
{:my.config/id :s1, :my.config/host "", :my.config/port 5555} - failed: (or (nil? %) (sequential? %)) spec: :my.config/server
Not sure why this one took me a minute to grok. Thanks @U04V70XH6!
Yup, you got it!
s/cat
in particular, since it can describe a sequence that, say, starts with one or more ints, followed by a string, an optional keyword, and then zero or more ints.
Hey guys, I'm running into some issues responding a post request with form-data. Does anybody mind helping me out? lmao
@dcsoups make sure you have type= da-type
set on each form input element.
@dcsoups do you have any more info like what errors your getting or what the server is receiving?
I was playing with the REPL a couple of days back and wanted to map a collection such that each item in the original list is repeated 3 times in the result. So, for example, 1 2 3
would become 1 1 1 2 2 2 3 3 3
. My first attempt didn't compile:
(mapcat #([% % %]) [1 2 3])
After some head scratching, I found my way to the clojure docs, which mention this very problem. They recommend:
(mapcat #(vector % % %) [1 2 3])
But, I like using the literal vector syntax, which I can still use if I do this:
(mapcat #(do [% % %]) [1 2 3])
Now, this certainly works, but is it acceptable Clojure code?there's also repeat
user=> (mapcat (partial repeat 3) (range 5))
(0 0 0 1 1 1 2 2 2 3 3 3 4 4 4)
(partial repeat 3)
could be replaced by #(repeat 3 %)
if you prefer
I would say it isn't idiomatic. Folks would either use vector
or the (fn [x] [x x x])
style.
In general, the set of places where #(..)
function literals are "acceptable" is pretty small. Stuart Sierra talks about this in his Do's and Don't series on his blog.
For example https://stuartsierra.com/2019/09/11/clojure-donts-short-fn-syntax and https://stuartsierra.com/2019/09/15/clojure-donts-numbered-parameters
thanks for the links - i'll add them to my bedtime reading
@sova my bad for the late response. I've been using ajax core to post form data here :
The format of the form-data is something like :
{:tpm/name "David Campbell", :tpm/email "<mailto:[email protected]|[email protected]>", :tpm/jira "nil", :tpm/productGroup "Placeholder", :tpm/pods #{"Placeholder"}}
And this is how I'm attempting to respond server side:
On the server side I'm using retit.swagger to respond to the post
If I write the following code: ^int (+ 1 2)
, it seems like the reader grabs the value of int
at read time and attaches it as metadata to (+ 1 2)
. So with that in mind:
Is there any way to add new values at read time?
You can put anything you like in there, as a map
That's the case for any ^:keyword
form. See the bottom of the docs page, it has all the variations. (I THINK anyway... going to check)
Yeah, that's how it works.
> (def ^:foobar testing 42)
#'user/testing
> (meta #'testing)
{:foobar true, :line 1, :column 1, :file "NO_SOURCE_PATH", :name testing, :ns #object[clojure.lang.Namespace 0x17ae98d7 "user"]}
@rsprangemeijer & @emccue of course, but in the docs it indicates that identifiers evaluate to their value, rather than the name itself.
https://github.com/typedclojure/typedclojure/blob/main/typed/clj.reader/README.md https://github.com/typedclojure/typedclojure/blob/main/typed/clj.lang/README.md I know that typed clojure has some form of extensible reader thing, but idk how mature that is
so even when the symbol evals to a keyword, if its not a literal keyword it is put into the tag
it depends how you are running your program
if you AOT compile, no.
(for reference, this is the page for meta
I was looking at: https://cljs.github.io/api/syntax/meta)
Like, I would like to be able to place them anywhere I could normally place an arbitrary clojurescript form.
(Which is why meta
seems to suffice, if I can just figure out where its environment comes from.)
Here's an example:
(ns my.namespace)
(defvisr component
(render [this updater] ...)
(elaborate [this] ...))
...
^{:editor :true
:construct my.namespace/component} (component+elaborate state)
Here (defvisr ...)
can expand to whatever is needed to make my.namespace/component
in the reader environment.
Right now, I have the defvisr
expand to:
(defvisr NAME
(render ...)
(elaborate ...))
; =>
(defn NAME+render ...)
(defmacro NAME+elaborate ...)
But again, that part is a little more flexible.The macro component+elaborate
doesn't really need to know about the read-time information of component
.
The problem is, going back to the sample:
(ns my.namespace)
(defvisr component
(render [this updater] ...)
(elaborate [this] ...))
...
^{:editor :true
:construct my.namespace/component} (component+elaborate state)
Where does the binding for my.namespace/component
come from? It looks like in clojure it can come from the defvisr
, since the reader and runtime share the same environment. But in clojurescript (even self-hosted?) that doesn't seem to be the case?so if defvisir
expands to defprotocol
and that protocol is bound to component
, it doesn't exist anywhere
(Right now the reader I'm using is indexing-push-back-reader
: https://cljdoc.org/d/org.clojure/tools.reader/1.3.2/api/cljs.tools.reader.reader-types)
if defvisir
expands to something "real" like def
, defn
or defrecord
then those can exist later
component+elaborate is expanded at compile time so it should have a "real" thing for the protocol
@dcsoups I think :handler
ought to be a function that will do something with the data you are providing to the post endpoint. You probably also want to use :body
instead of :description
Most HTTP responses need something that is a :status
and a :body
... your :handler
ought to be something like a function that prints out the data. Like in this image, the keys on the :body
that's :post
ed, they are destructured via :keys [x y]
so you could do something similar with all the variables you mentioned.
["/slack-modify"
{:post {:summary "Update ze slack channels."
:responses {200 {:body "Success!"}
400 {:body "Oops"}}
:handler (fn [{{{:keys [your keys here]} :body} :parameters}] (println your keys here))
:accept :text
:debug? true
:throw-entire-message? true}}]
I think that ought to work...Hi, Anyone has experience using MongoDB with Clojure? What is the best way to connect to MongoDB using Clojure ? I was looking at Monger. But it seems that its not very updated as Java MongoDB driver. I was wondering if maybe the best way is to do some Java interop.
@wcarvalho.ferreira i dont know the case of monger but from my observation clojure libraries have often much less reason to change - are usually very stable and thus dont see as many commits/updates as comparable libraries in languages like java
Oh Ok, I just thought that the Clojure lib should have the same updates as the Java driver. But thanks anyway
Hi just got the new pragprog Clojure book and want to give clj another shot. But I’m a vim user and that conflicts with Calva. Anyone know a way to disable the VS Code vim extension when a Clojure buffer is active? Or another good workaround? I don’t want to tool around with Neovim for a long time setting up a new Clojure env in that, if possible
I had pretty good success with using the https://github.com/asvetliakov/vscode-neovim extension for vim in vscode together with calva
i think there's a #calva channel that would have some people that can help you get your environment configured
@U6JS7B99S thanks, vscode-neovim was pretty buggy for me when I tried it a few weeks ago but maybe I’ll give it another shot
It was a bit buggy for me lately before I properly updated neovim, calva and the plugin but you may be using festures I dont
Oh nice are you on 0.5 stable of neovim? I was using an RC of 0.5 head, using a clean init.vim
I just updated to stable and applied the maps on https://calva.io/vim/, will give a try thanks!
@US948FXDL yes, im on 0.5.0 stable
@U6JS7B99S What kind of rebinding did you do? I’m having trouble getting paredit mode to work for me with the Vim cursor. Either it’s buggy or I am not understanding paredit mode
@US948FXDL there is a specific paredit mode? Im using mostly the basic slurp/barf sexp stuff from calva. No special mode for it. That said I can dm you my bindings
@wcarvalho.ferreira I think Monger is the most up-to-date MongoDB wrapper at this point. I thought it had all been updated for the 3.0 drivers but it's been a while since I last looked. We were very heavily into MongoDB for years but we abandoned it completely and migrated back to MySQL... I used to maintain CongoMongo but I would pretty much always recommend folks use Monger instead (once it dropped its dynamic var API).
Thanks @U04V70XH6 I will take a careful look at Monger and see if I can use in my project.
Looks like the java drivers are already on 4.x though and monger still on 3.x :thinking_face:
It certainly hasn't helped the 2.x -> 3.x was a load of breaking changes and I wouldn't be surprised if 3.x -> 4.x also has breaking changes. But as long as the 3.x drivers work with whatever actual server version of MongoDB you're using, you should be OK. We ran with the 2.x drivers against the 3.x server for a while I think...? It was years ago...
Ok ... thanks again @U04V70XH6
Hi. I'm learning clojure. It's being fun. How can I have a better REPL? Currently my default clojure -r
REPL doesn't support arrow keys to navigate, which is pretty limiting.
https://github.com/bhauman/rebel-readline is a pretty nice enhancement to that workflow
There may be something newer/fancier though
A bit +1 for Rebel Readline -- that's what I use for all my console REPLs. I have this in :aliases
in my ~/.clojure/deps.edn
file:
:rebel {:extra-deps {com.bhauman/rebel-readline {:mvn/version "RELEASE"}}
:main-opts ["-m" "rebel-readline.main"]}
So I can do clojure -M:rebel
and get a really nice syntax-colored REPL with auto-complete, inline help, and full arrow key/edit functionality. It's very nice.