Fork me on GitHub

what’s the proper way to check if a value is a sequence? seq? doesn’t work on vectors


seq? tests whether something is a seq (not a sequence). And a vector is not a seq. It depends on exactly what types of data you're trying to select for, but I suspect sequential? is probably what you want @grazfather



user=> (doc seq?)
  Return true if x implements ISeq
user=> (doc sequential?)
  Returns true if coll implements Sequential
user=> (doc seqable?)
  Return true if the seq function is supported for x


that works. Why are lists seqs but vectors are not?


I thought seq just meant an ordered collection


i guess not. How is ISeq different from Sequential, then?


Sequential is a "marker" interface -- it has no methods.


Right, I guess I just thought that vector would implement that interface


user=> (->> [1 2 3] type ancestors (sort-by str))
(clojure.lang.AFn clojure.lang.APersistentVector java.lang.Object clojure.lang.Associative clojure.lang.Counted clojure.lang.IEditableCollection clojure.lang.IFn clojure.lang.IHashEq clojure.lang.IKVReduce clojure.lang.ILookup clojure.lang.IMeta clojure.lang.IObj clojure.lang.IPersistentCollection clojure.lang.IPersistentStack clojure.lang.IPersistentVector clojure.lang.IReduce clojure.lang.IReduceInit clojure.lang.Indexed clojure.lang.Reversible 
clojure.lang.Seqable ; vector is seqable (you can call seq on it)
clojure.lang.Sequential ; vector is sequential java.lang.Comparable java.lang.Iterable java.lang.Runnable java.util.Collection java.util.List java.util.RandomAccess java.util.concurrent.Callable)


Clojure tries to avoid providing inefficient ways to access data. Vectors are efficient for conj at the end, lists are efficient for conj at the start, which is what cons does.


cool. thank you!


and til ancestors


So vector has peek and pop instead of first and next:

user=> (peek [1 2 3])
user=> (pop [1 2 3])
[1 2]


(and they operate on the end of the vector, not the start)


first works on vectors


Right, but it calls seq first and so you are coercing the vector into a sequence.


map etc also work on vector by coercing it to a sequence first.


alright. Thank you. I see seq? used a lot to test if something is a sequence, I was pretty suprised to see it not work on vectors. What’s the proper way to test that a value is iterable?)


I haven't seen seq? used very often. (seq xs) is common idiom to check if there are items in xs


It depends what you mean by "iterable". Vectors implement java.lang.Iterable but I suspect that's not what you mean?


I mean an ordered collection


Sounds like Sequential then.


works for me


Although maps and sets aren't sequential?, but they are seqable?


> (sequential? {})
> (sequential? #{})
> (seqable? {})
> (seqable? #{})


user=> (for [coll [(list 1 2 3) [1 2 3] #{1 2 3} {1 2, 3 4}] f '[seq? sequential? seqable?]] [((resolve f) coll) f coll])
([true seq? (1 2 3)] [true sequential? (1 2 3)] [true seqable? (1 2 3)] [false seq? [1 2 3]] [true sequential? [1 2 3]] [true seqable? [1 2 3]] [false seq? #{1 3 2}] [false sequential? #{1 3 2}] [true seqable? #{1 3 2}] [false seq? {1 2, 3 4}] [false sequential? {1 2, 3 4}] [true seqable? {1 2, 3 4}])
user=> (clojure.pprint/pp)
([true seq? (1 2 3)]
 [true sequential? (1 2 3)]
 [true seqable? (1 2 3)]
 [false seq? [1 2 3]]
 [true sequential? [1 2 3]]
 [true seqable? [1 2 3]]
 [false seq? #{1 3 2}]
 [false sequential? #{1 3 2}]
 [true seqable? #{1 3 2}]
 [false seq? {1 2, 3 4}]
 [false sequential? {1 2, 3 4}]
 [true seqable? {1 2, 3 4}])


I agree. I would say that if code is using seq? it is either doing something very specific or it is wrong 🙂


but seq crashes if its a different type like true


There's seqable? to test for things that can be seq'd.


user=> (seqable? true)


alright, thanks again


Seqs are a logical list abstraction


Vectors (and maps and sets etc) can provide a seq view (a logical list) over those collections


I realise I'm not too sure what functions I can use to combine 2 lists together but anyway I have a list like so ...

`((171) (0 0 0 0 0 0)
  (147) (0 0 0 0)
  (183) (0 0 0 0 0 0 0))
how do I transform the list into something like
`((171 0 0 0 0 0 0)
  (147 0 0 0 0)
  (183 0 0 0 0 0 0 0))
Like to merge pairs of elements successively


(Im not a clojure expert) maybe this way?

(->> `((171) (0 0 0 0 0 0)
       (147) (0 0 0 0)
       (183) (0 0 0 0 0 0 0))
     (partition-all 2)
     (map (fn [[left right]]
            (concat left right))))


Right! That's definitely 1 way 🙂


Transducer version (into [] (comp (partition-all 2) (map flatten)) [[1 2] [3 4] [5 6] [7 8]])


@UUSQHP535 as a general tip, stick to vectors rather than lists or sequences unless there’s a specific need; easier to reason about IMO


Note that flatten recursively flattens nested sequences - I would go with (map #(apply concat %))

👌 2

(concat '(171) '(0 0 0 0 0 0)) ;; '(171 0 0 0 0 0 0)

Endre Bakken Stovner05:04:35

Is there a way to make Clojure always print seqs as a literal? I am copy-pasting a lot of output into the REPL and I always forget to add ' in front of seqs. (`(:hi :there)` triggers an error as you know.)


Prefer vectors where possible


mapv, vec functions come in handy

Endre Bakken Stovner05:04:47

I should make my own print that uses Specter or some such to find and turn seqs into vecs.

Endre Bakken Stovner05:04:21

But sometimes I want the colls to actually be seqs 🙂


Almost everything you can do with lists/seqs you can do with vectors, but you avoid the copy paste problem


You might be able to simply quote everything you print. Quoted maps and vectors read back "correctly" too. Are you just printing random stuff with all the print functions? Or are you using tap?

Endre Bakken Stovner07:04:47

I am not using tap. I've just heard the term. How is it applicable here?


You might be able to add your quotes with add-tap

Endre Bakken Stovner08:04:06

I'd like to make selmer.parser/render join lists of strings before rendering, but leave strings as is:

(render "{{input}}" {:input ["a.txt" "b.txt"]}) => "a.txt b.txt"
(render "{{input.1}}" {input ["a.txt" "b.txt"]}) => "a.txt"
Currently the first would print "[&quot;a.txt&quot; &quot;b.txt&quot;]". I'd like to avoid having to call {{input|join\" \"}} or do it implicitly.

Endre Bakken Stovner08:04:14

This is the code selmer uses to render:

(defn render-template [template context-map]
  " vector of ^selmer.node.INodes and a context map."
  (let [buf (StringBuilder.)]
    (doseq [^selmer.node.INode element template]
        (if-let [value (.render-node element context-map)]
          (.append buf value)
          (.append buf (*missing-value-formatter* (:tag (meta element)) context-map))))
    (.toString buf)))
And node is
(deftype TextNode [text]
  (render-node [this context-map]
    (str text))
  (toString [_]
    (str text)))
Does this make it possible to inject my desired code or override the existing implementation? Never made or extended protocols before.

Endre Bakken Stovner10:04:08

(deftype L [l]
  (toString [self] (str/join " " l)))

(render "all: {{lst}} one: {{lst.1}}" {:lst (L. [1 2 3])})
"all: 1 2 3 one: "
Now I need to find out how to implement whichever method is needed to pick out elements.

replied to a thread:

Ok, I figured out something which is crufty and almost 100% sure not the correct way but:

(defn custom-v [& i]
 (proxy [clojure.lang.IPersistentVector] []
  (seq [] i)
  (nth [idx not-found]
   (nth i idx not-found))
  (toString [] (apply str i))))
(custom-v 1 2 3)
=> [1 2 3] (str (custom-v 1 2 3)) ;custom => “123” (.toString (custom-v 1 2 3)) ;custom => “123" (nth (custom-v 1 2 3) 0 :not-found) => 1 (nth (custom-v 1 2 3) 42 :not-found) => :not-found You get “vectors” that are not really vectors; they satisfy, say, (vector? …) but many of the Clojure functions that work on vectors, like (rseq …) don’t work in this case; It throws UnsupportedOperationException (as expected) I’m actually curious; Is there an easy/straightforward way to implement, let’s say, a custom (str …) for a specific Clojure collection without “losing” functionality?

👍 1

is that your actual goal?


if so, you can do that by overriding the print-method for the type


for creating custom collections, there are a variety of different interfaces you might need to implement to replicate different parts of collection functionality


That’s what I thought. So the short answer is: no, there’s no “one liner” way to extend vectors….


you can see what normal vectors implement for example:


user=> (ancestors clojure.lang.PersistentVector)
#{clojure.lang.IPersistentCollection java.lang.Iterable clojure.lang.Sequential clojure.lang.ILookup clojure.lang.APersistentVector java.util.List java.lang.Runnable clojure.lang.Indexed clojure.lang.IPersistentVector java.lang.Comparable clojure.lang.IReduce clojure.lang.Associative clojure.lang.IMeta clojure.lang.IEditableCollection clojure.lang.IObj clojure.lang.IFn java.util.concurrent.Callable clojure.lang.AFn java.lang.Object java.util.Collection java.util.RandomAccess clojure.lang.Reversible clojure.lang.IKVReduce clojure.lang.IHashEq clojure.lang.Counted clojure.lang.Seqable clojure.lang.IReduceInit clojure.lang.IPersistentStack}


Ah, that’s neat to know; never knew of that fn


if you're interested in this topic, I wrote a section in Clojure Applied about it with a picture that maps Clojure api functions to methods in the internal interfaces


Very cool, will check it out. Thank you for your input Alex, always the trusted source clj 🙂


this is kind of a cool helper function too:


will create a deftype scaffold with a lot of the necessary stuff for a custom type (you will still need to tailor it appropriately but can help a lot)

👍 1

No; I went on a Java interface/class rabbit hole exploration 🙂 And trying to figure out if there’s a generic “proper” way to do it; or it would involve re-writing everything as protocols;


(in general, I'd say this topic is not a #beginners topic and probably better asked in #clojure instead)


Yes… It stemmed from a #beginners conversation… I agree


Hi 👋, I'm learning about macros.  After doing the basic conditional tutorials, I now want to write some more "advanced" macros to wrap my head around the topic. My goal is to write a macro that returns a function that combines str/replace functions.

;; It should later work like this:
((combine-replace #"a" "A" #"b" "B") "ab") => "AB"
I started with the base case
(defmacro combine-replace [x y & xs]
   `(fn [s#]
      (-> s#
          (str/replace ~x ~y))))

 ((combine-replace #"a" "A") "ab") => "Ab"
what I now want is to "write"  (str/replace ~x ~y) n times for all pairs in xs. The generated code should look something like this
(-> s#
    (str/replace ~x ~y)
    (str/replace ~x2 ~y2)
Currently, I'm sitting on this
(defmacro combine-replace [x y & xs]
   `(fn [s#]
      (-> s#
          (str/replace ~x ~y)
          [email protected](loop [xs# xs]
              (when xs#
                `(str/replace ~(first xs#) ~(second xs#))
                (recur (nnext xs#)))))))
can someone help me to understand what I actually have to do, to make this work?


maybe this will hint what your a looking for:

(let [xys [#"1" "2" #"3" "4" #"5" "6"]]
  (assert (= (mod (count xys) 2) 0) "Expression must be multiple of 2")
  (->> xys
       (partition 2)
       (map (fn [[left right]]
              `(some-fun ~left ~right)))))


you gotta move the xs handling outside of the syntax unquote:

(defmacro combine-replace [x y & xs]
  (let [replaces (for [[x1 y1] (partition 2 xs)]
                   `(str/replace ~x1 ~y1))]
    `(fn [s#]
       (-> s#
           (str/replace ~x ~y)
           [email protected]))))


@U487Z32UF good call with the assert. I forgot that partition handles odd-count seqs

✌️ 1

Thank you both so much 🙏❤️

👍 2
Scott Starkey16:04:19

OK, I seem to be having Java “classpath” problems when trying to load external libraries. Am I doing something wrong? 1. First I add to the :dependencies section of my project.clj file. 2. Then I do a lein deps. It seems to load the files from clojars. 3. Then I add the library line(s) in my (ns … (:require … )) . But I get a classpath error: Could not locate ring/middleware/json__init.class, ring/middleware/json.clj or ring/middleware/json.cljc on classpath. Probably a dumb newbie mistake, but what am I doing wrong? (Images included.) It makes me think that my classpath is missing something.


you don't need to run lein deps


my guess is whatever repl your editor is connected to is either not based on your project.clj, or hasn't been restarted since you added the dependencies


Hey guys, so I'd like to use something datomic like for a desktop application I was building. Apparently datomic is not free software, but there are libraries out there that behave like datomic. What are some of the lesser known options out there? So far I know about... • Datahike • Datascript • Datalevin

Scott Starkey18:04:50

@hiredman - You are totally right. I exited completely out of Emacs, and then back in, opened up a REPL, and magic! 🎩 Thanks for your help.


how would i do the following in clojure (reading a js textbook but can't help myself thinking what the clojure equivalent would look like) ... It finds the first number that is both greater than or equal to 20 and divisible by 7.

for (let current = 20; ; current = current + 1) {
  if (current % 7 == 0) {
best I could come up with is
(first (filter #(zero? (mod % 7)) (iterate inc 20)))
doesn't "feel" right


a literal translation: (loop [current 20] (if (zero? (mod current 7)) current (recur (inc current))))


ah loop recur! i rarely use it. Still looks verbose


(println 21) ?


:rolling_on_the_floor_laughing: love it

😆 3

regarding verbosity, I can't think of the higher level construct that would reduce the size of the code significantly


in either the filter or the loop version, there's not much noise - every expression does something meaningful


agreed but i feel i'm overlooking something take, some, take-while, for etc


i like this one but

(some #(when (zero? (mod % 7)) %) (iterate inc 20))
just replaces first, filter with some, when


right, some just makes it more complicated