Fork me on GitHub
#clojure
<
2023-03-26
>
lilactown01:03:35

(list? (rest `(1 2 3)))
;; => false
why

bronsa02:03:47

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.

bronsa02:03:56

rest returns a seq . not all seqs are IPersistentLists

bronsa02:03:26

list? is not as useful a predicate in clojure

lilactown02:03:35

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 seems

bronsa02:03:30

use seq? instead of list?

lilactown02:03:42

user> (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...

borkdude09:03:20

list? is 99.99% never what you want for checking "is this a list", I'd almost write a linter for this

Thierry09:03:13

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?

p-himik09:03:17

Just plain Linux here.

rickheere09:03:49

For me just arch Linux with vscodium

Thierry09:03:41

Do you develop on a laptop or desktop? Is your dev machine the same machine as your main home machine?

p-himik09:03:08

Mainly desktop but laptop when traveling. Same machine for everything.

rickheere09:03:47

Samen laptop for everything

practicalli-johnny09:03:03

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.

Ben Sless09:03:52

Linux on laptop and PC, pop os, java versions managed with jenv, clojure and lein installed manually, emacs as ide

pez09:03:25

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.

hifumi12310:03:28

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.

Mario G12:03:40

Mac M1 at work; Linux and (sometimes) an older Mac for my own time

Alex Miller (Clojure team)13:03:15

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

armed15:03:31

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)

seancorfield17:03:34

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.

wombawomba12:03:43

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?

Jan K15:03:49

If you use the undocumented reader macro #=(...) and AOT it should do what you want: (def version #=(->> ...)

wombawomba15:03:12

sweet, thanks!

wombawomba16:03:22

FWIW sticking the full expression in #=(->> ..) doesn't work

Jan K16:03:18

Huh, I don't know why it wouldn't work either way 🤷:skin-tone-4:

vemv20:03:07

> 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

phill20:03:43

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?

marrs13:03:56

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?

genmeblog13:03:28

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

marrs13:03:19

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.

genmeblog14:03:52

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)

marrs08:03:35

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.

marrs08:03:22

but more generally it would just be for consistency with the single arg variant, and maximum utility

genmeblog08:03:43

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)).

marrs08:03:30

Oh, interesting. (namespace (keyword "foo/bar" "foo/baz")) yields "foo/bar"

genmeblog08:03:05

(keyword "a/b/c/d/e")
;; => :a/b/c/d/e

marrs08:03:35

Yeah, and calling namespace on that will yield "a"

marrs08:03:59

I've been taking advantage of that "feature" to my own ends

genmeblog08:03:42

Yeah, there are some more keyword quirks. https://clojure.org/guides/faq#keyword_number

👍 2
genmeblog08:03:55

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.

marrs08:03:19

indeed. Thanks

whiterose19:03:37

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?

whiterose19:03:09

Updating ring sorta seems to be working. Will keep posted if I make any progress.

hiredman19:03:31

There are jetty ring adapters for newer versions of jetty https://github.com/sunng87/ring-jetty9-adapter

whiterose15:03:26

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?

kennytilton22:03:15

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.

hifumi12322:03:48

I would love a CL utility library and possibly contribute to it.

hifumi12322:03:17

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.

pppaul22:03:56

I don't really understand what the clojure example is doing, so it's hard to compare to the lisp ones.

kennytilton23:03:21

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.

pppaul23:03:03

could you post the objects and output of the function?

kennytilton23:03:09

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.

👍 2
kennytilton23:03:24

(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

hifumi12323:03:20

Okay I just came up with a naive implementation of cl-find

hifumi12323:03:09

(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))))))

hifumi12323:03:30

So far it works for (cl-find 2 [7 8 9 10] :key #(mod % 7))

hifumi12323:03:20

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))

hifumi12323:03:08

I’m also running into an interesting problem where I want FUNCALL in order to implement FIND-IF, but Clojure doesn’t have FUNCALL 😄

hifumi12323:03:57

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….

hifumi12300:03:33

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])

kennytilton01:03:44

"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?

hifumi12301:03:14

> What does funcall offer? an easy implementation of FIND-IF in terms of FIND 🙂

hifumi12301:03:39

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

👍 2
kennytilton07:03:57

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?

hifumi12308:03:40

It is posted earlier in this thread

Matthew Downey15:03:46

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

cheewah23:03:13

use specter? https://github.com/redplanetlabs/specter

(use '[com.rpl.specter :refer [select-first ALL]])
(select-first [ALL even?] [1 2 3 4])
; 2