This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-03-26
Channels
- # babashka (12)
- # beginners (53)
- # cider (6)
- # clj-kondo (2)
- # cljdoc (18)
- # clojars (6)
- # clojure (72)
- # clojure-europe (27)
- # clojurescript (85)
- # component (2)
- # conjure (4)
- # datalevin (43)
- # graalvm (8)
- # hyperfiddle (17)
- # lsp (72)
- # malli (5)
- # off-topic (1)
- # pathom (6)
- # perun (6)
- # polylith (10)
- # releases (1)
- # shadow-cljs (30)
- # xtdb (10)
user=> (doc list?)
-------------------------
clojure.core/list?
([x])
Returns true if x implements IPersistentList
user=> (doc rest)
-------------------------
clojure.core/rest
([coll])
Returns a possibly empty seq of the items after the first. Calls seq on its
argument.
I'm writing a macro that uses the same synax as fn
for arity.
(my-macro [] ,,,)
(my-macro [_] ,,,)
(my-macro ([] ,,,))
(my-macro ([] ,,,) ([_] ,,,))
I'm trying to detect what arity is passed to it. what predicate should I use?
I was trying
(cond
(vector? (first body)) (count (first body))
(and (list? (first body)) (list? (second body))) :multi
(list? (first body)) (count (ffirst body)))
but the types are wrong it seemsuser> (seq? '(1 2 3))
true
user> (seq? (cons 0 '(1 2 3)))
true
user> (seq? (rest '(1 2 3)))
true
gotcha, for some reason I thought seq?
was the more specific one for some reason and was thus avoiding it...list?
is 99.99% never what you want for checking "is this a list", I'd almost write a linter for this
I have a general question. As Clojure developer, what does your typical development environment look like? Do you work in Windows or Linux? Do you work in a Linux VM on Windows or in a dedicated Linux os? In either way, what Linux distro do you use? What virtualization software do you use? VMWare, VirtualBox, Hyper-V or other? At the moment I use Windows 11 with VSCode and WSL1 for testing and deployment. I tried using WSL2 with VSCode but its seemingly slow. I am considering using a vm with either vmware workstation or hyper-v with lubuntu or debian. For the record, I do all my compiling for other languages on a dedicated linux box and my home server runs on debian with docker containers. No shortage on knowledge, just interested in what everyone uses as main dev environment. Your thoughts?
Do you develop on a laptop or desktop? Is your dev machine the same machine as your main home machine?
Lenovo Extreme 2 Laptop with Regolith Linux (Ubuntu+i3) Clojure CLI and my config with lots of additional aliases to extend the tooling Emacs (Spacemacs) and Neovim (Conjure) for editing, with Clojure LSP (Portal and other tools via a custom user namespace) Cljstyle & MegaLinter linting (driven by make locally and CI via GitHub workflows) Docker for local service build & orchestration (only when there are multiple local services and system integration work/testing) I had to use Microsoft windows with WSL in the past, with Clojure CLI and Emacs. It ran okay, but not as nice as Linux.
Linux on laptop and PC, pop os, java versions managed with jenv, clojure and lein installed manually, emacs as ide
The question isn’t general enough for my answer 😀 Mac. Everything Clojure I run natively. Databases and web servers and stuff I run in Docker.
IIRC the Clojure community survey asks a lot of questions like this, and provides useful insight into how the community is doing things at a given point in time. Results for this year should be out in a few months, I think. With that said, I personally work on a Mac with Emacs and Docker. My software deploys to an Alpine Linux container, and that container is then managed by Kubernetes.
Typical survey results are 55% Mac, 35% Linux, 10% windows (split in half w or w/o wsl). I don’t have this years results handy but they are in that ballpark
Currently (since September 2022) I’m pretty happy with the following setup: • Intel Mac (waiting for Apple M3) • Kitty + Tmux + Neovim with Conjure and a bunch of other plugins • asdf-vm for managing versions of everything (jvm, node, clojure, neovim)
Re: WSL1 vs WSL2 - with the former, you do better if your files are on Windows, with the latter you do better if your files are on Linux. My dev env - on both my work desktop and my OSS laptop - is VS Code on Windows with everything Clojure-related on WSL2/Ubuntu, with Docker to run third-party services like Elastic Search, Percona, and Redis. Works great.
Is there a way to reliably store data from the environment at build time? I'd like to extract the version number of a library and embed it in the code itself, i.e. something like:
(defonce version
(->> (slurp "project.clj")
(re-find #"defproject.*\"([^\"]+)\"")
second))
I figured if I put this in its own namespace and AOT it, that should do the trick. Alas, it seems like it gets re-evaluated when it's loaded in projects that use the library. Any pointers?If you use the undocumented reader macro #=(...) and AOT it should do what you want:
(def version #=(->> ...)
sweet, thanks!
FWIW sticking the full expression in #=(->> ..)
doesn't work
> Is there a way to reliably store data from the environment at build time?
read it from an io/resource
. Spit such a resource at build time. You can have a dev convenience placed at dev-resources
so that the same code works in all envs
When it comes time to package a work as an uberjar (include this, exclude that, resolve such-and-such a conflict by appending all the files, show a splash picture while loading, etc. etc), Maven plugins offer more of the essential monstrosities than "lein uberjar". So even when Leiningen, and its fabulous "checkouts" and "lein test" features, are used in development, the serious work of embedding a version number may usually arise in the context of Maven rather than Leiningen. In that case, you can use what Maven calls "filtered resources", of which one is a file that may say just "{mvn.version}" or something along those lines; Maven populates it and your program loads the resource. Perhaps someone has already made a Lein plugin to do something compatible?
Here's a curious thing: (keyword :foo)
returns :foo
but (keyword :foo :bar)
results in a type error. Only strings are allowed when 2 args are passed. Anyone know why?
Two args version accepts namespace as first argument which should be a string. Keyword itself can be created from keyword, string and symbol. See the source: https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L620
Yes, but the second arg must also be a string. Also, every char allowed in a keyword can also be present in a string, so I don't see what advantage is gained from requiring a string for the namespace either.
ok, indeed (https://github.com/clojure/clojure/blob/clojure-1.10.1/src/jvm/clojure/lang/Keyword.java#L53). Anyway, I don't see the reason for calling (keyword :foo :bar)
Well, in my case I'm constructing the keyword at runtime and the data I receive for the namespace part happens to be provided as a keyword.
but more generally it would just be for consistency with the single arg variant, and maximum utility
Keywords and symbols can be namespaced already, so the (keyword :a/b :c/d)
is confusing. For your case you can call (keyword (name keyword-one) (name keyword-two))
.
Yeah, there are some more keyword quirks. https://clojure.org/guides/faq#keyword_number
especially:
> [...]no validation check is performed on the inputs to keyword
(or symbol
), which makes it possible to create keywords that, when printed, cannot be read back into a keyword (due to spaces or other non-allowed characters). If this is important to you, you should validate the keyword inputs first, before creating the keyword.
I'm facing an issue with a transitive dependency. Both ring
and aws-api
using jetty-http
in a way. aws-api
is using it through jetty-client
and ring
is using it through jetty-server
. What is a good way to resolve this conflict?
There are jetty ring adapters for newer versions of jetty https://github.com/sunng87/ring-jetty9-adapter
I just updated the ring, excluded jetty-http from both aws-api and ring. The added a common jetty-http. It worked and unit tests were passing so I believe it should be good.
Though it was kinda confusing. The jetty-http wasn't showing up under the ring as dependency when I did lein deps :tree
. It was only showing the jetty-server and I had to go into the jetty-server GitHub and dig through dependency to find which is the common package between jetty-client and jetty-server.
Is this common or am I missing something?
One thing I miss about Common Lisp is the API of what I think the smart kids call "list comprehensions". For example, writing this is no fun:
(some #(when (= (mget me :route) (:route %)) %)
lessons)
...when I could in CL write:
(find (mget me :route) lessons :key :route)
...and if =
did not work as my comparator, I could write:
(find (mget me :route) lessons :key :route :test #'whatever)
Even better, all the CL list functions have the exact same API.
Anyone mind if I do a CL
utility? I started on one when I first encountered Clojure, and after ten years of when in Rome do as Romans do I think maybe a thin wrapper on CLJ list functions is still valuable.Though I’m not sure what added value we get in being able to choose comparators in Clojure. In Common Lisp we have to be careful because e.g. a hash-table defaults to EQL, so if we use string keys we’d need STRING= or EQUAL. Meanwhile = in Clojure more or less just works like EQUALP and doesn’t need us to worry much. I guess what I’m trying to get at here is that I can’t recall the last time = wasn’t enough for me in Clojure.
I don't really understand what the clojure example is doing, so it's hard to compare to the lisp ones.
The CLJ example picks out the first lesson with the matching route. To do this, we need to find the lesson, then (my whine) explicitly return it. CL list functions all understand the :key idea, which is "find the elt where (key elt) matches the value for which I am searching.
Yeah, @U0479UCF48H, the CLJ =
covers more ground than CL eql
. But if we are going to call it cl-find
, we should match the API.
(some even? [1 2 3]) => true
Bummer. I wanted the first even number.
(some #(when (even? %) %) [1 2 3]) => 2
Yay.
If cl-find
existed:
(cl-find even? [1 2 3]) => 2
...and
(cl-find 2 [7 8 9 10] :key #(mod % 7)) => 9
(defn cl-find
"Search for the `item` in the `sequence` bounded by `start` and `end` that
satisfies the test `test` or `test-not`. If `from-end` is true, then we return
the rightmost element satisfying the test. A `key` function can be supplied
for extracting a value before the test is computed."
[item sequence & {:keys [from-end test test-not start end key]
:or {from-end nil
test =
test-not not=
start 0
end nil
key nil}}]
(assert (or (list? sequence)
(vector? sequence))
"The provided sequence must be a list or vector.")
(let [search-sequence (cond->> sequence
(some? start) (drop start)
(some? end) (drop-last end)
(true? from-end) reverse)
maybe-key (or key identity)]
(loop [continuing true
result nil
[x & xs] search-sequence]
(if (or (not continuing)
(nil? xs))
result
(if (or (test item (maybe-key x))
(not (test-not item (maybe-key x))))
(recur false x xs)
(recur true nil xs))))))
to be more in-line with common lisp behavior, I should also (assert (or (list? sequence) (vector? sequence)))
… I also just noticed my termination condition should be something like (or (not continuing) (nil? xs))
I’m also running into an interesting problem where I want FUNCALL in order to implement FIND-IF, but Clojure doesn’t have FUNCALL 😄
Oh, since the :test-not
key and find-if-not
function is deprecated, I wonder if people will be okay with an implementation of this function in Clojure leaving out those options. As far as I know, there are functions like remove-if-not
that are deprecated but nonetheless allow some code to be easier to read, so a lot of people will use these functions anyway, and will probably be surprised if they’re absent from a Clojure implementation. Hm….
This should work for FIND-IF
(defn cl-funcall [f & args]
(apply f args))
(defn cl-find-if [test sequence & {:keys [from-end start end key]}]
(cl-find test sequence
:test cl-funcall
:from-end from-end
:start start
:end end
:key key))
It works at the very least for (cl-find-if even? [1 2 3 4])
"an interesting problem where I want FUNCALL in order to implement FIND-IF," Wait. What does funcall offer? CLJ is a Lisp-1, we should leverage that, no?
I also just realized my cl-find
function can be slightly faster if we decide to rseq
instead of reverse
when the input is a vector
Still do not see why we need funcall. Don't need it here, eg:
(defn find-if [test coll]
(some #(when (test %) %) coll))
What does your find
look like?I like using the https://github.com/ptaoussanis/encore/ library from @UASETR481 for all sorts of stdlib extensions like this. E.g.
(enc/rfirst even? [1 2 3]) ;=> 2
It’s also in medley as find-first: http://weavejester.github.io/medley/medley.core.html#var-find-first
use specter? https://github.com/redplanetlabs/specter
(use '[com.rpl.specter :refer [select-first ALL]])
(select-first [ALL even?] [1 2 3 4])
; 2