Fork me on GitHub
Piotr Brzeziński11:01:09

I’m going through 4clojure exercises and was asked to create palindrome checker. What I’ve done is

(defn palindrome? [v]
  (loop [original v reversed (reverse v) result false]
    (if (empty? original)
      (if (= (first original) (first reversed))
        (recur (rest original) (rest reversed) true)
        (recur (rest original) (rest reversed) false)))))
then I went to see other’s solutions and I ran into
(defn palindrome? [v] (= (reverse v) (reverse (reverse v))))
and I felt stupid 😄

😀 9

There’s a bug in your code. It looks like you will report if the last and last item are the same

👍 3
Piotr Brzeziński15:01:37

Ohh, great catch, thank you.


They could've substituted (reverse (reverse v)) with (seq v).


know what you feel. IM a little but further on 4 clojure and still feel stupid @peb.brzezinski

Piotr Brzeziński11:01:17

I mean I’ve been a developer for couple of years and I still get that feeling every day 😄


oke, so not wierd that I feel like a super beginner a lof when solving learning challenges

Piotr Brzeziński11:01:47

How does that work @jaihindhreddy? (= (seq [1 2 3]) (reverse [1 2 3])) why are they equal? :thinking_face:


seq returns a sequence from its argument. reverse returns a sequence of the elements in its argument in the reverse order. So, if we do (defn palindrome? [s] (= s (reverse s))), it won't do the right thing for strings, because the string "aba" and the sequence of characters '(\a \b \a) don't compare as equal. So, either seq or calling reverse twice on the input "converts it" to a seq, in a manner of speaking. See, and in the docs for more details.. Turns out I made a mistake. seq returns nil when the argument doesn't have anything in it. So, my impl fails for the empty string, because (reverse "") returns the empty list, which is not the same as nil. So, this would be a correct implementation:

(defn palindrome? [s]
  (= (seq s)
     (seq (reverse s))))
That being said, if we wanted this function to work only with strings, this would be a more efficient way to do it:
(defn palindrome? [s]
  (= s (clojure.string/reverse s)))
PS: Here's a weird way I found to implement the same:
(def palindrome?
  (comp zero? dec count set (juxt seq (comp seq reverse))))

Piotr Brzeziński11:01:31

Nice, thanks a lot for the in detail explanation!

Piotr Brzeziński11:01:46

Point free FTW 🙂


4clojure has some amazing and very stupid examples of point free programming


(it's a fun exercise to see why they work, but they are often terrible as code per se)

💯 3

anyway why not (comp zero? dec count set (juxt seq reverse) seq) (I almost put identity in there, but seq has a nice short name)

💯 3

@U051SS2EU that doesn't work on the empty string, because (reverse "") evals to (), which is not the same as nil.


it calls seq first so both are nil


oops! misread it 😅


(ins)user=> ((comp zero? dec count set (juxt seq reverse) seq) "aba")
(cmd)user=> ((comp zero? dec count set (juxt seq reverse) seq) "abc")


@jaihindhreddy actually no you are right, I had it wrong

(cmd)user=> ((comp zero? dec count set (juxt seq reverse) seq) "")
(cmd)user=> ((comp set (juxt seq reverse) seq) "")
#{nil ()}
because (reverse nil) evaluates to () :/


@peb.brzezinski what does reverse [1 2 3] returns ?

Piotr Brzeziński11:01:54

I know it returns (3 2 1)

Piotr Brzeziński11:01:04

But then I don’t get why (1 2 3) is equal to (3 2 1)

Piotr Brzeziński11:01:06

Seems like I need to read a bit about seqs. Anyway, thanks for making me dig deeper 🙂


@peb.brzezinski It’s not, because (1 2 3) is not a palindrome

👍 3

I have to repeat every character in a seq 2 times. Is this a good solution or can it be improved

(defn my-repeat2[s]
  (flatten(map (fn[item](repeat 2 item)) s)))

Daniel Stephens13:01:47

I'd prefer mapcat to map+flatten, I also don't often use flatten at all since if your items happened to be vectors as well it would give you something unexpected (try your fn with [[1 2] [3 4]]). Also for the sake of two items, I wouldn't bother with a repeat, but that's personal preference.

(defn my-repeat2[s]
  (mapcat (fn [i] [i i]) s))


What’s good or not depends on the tradeoffs you’re willing to make. I would strongly consider adding spaces to make it match more common spacing patterns:

(defn my-repeat2 [s]
  (flatten (map (fn [item] (repeat 2 item)) s)
For my favorite version of the implementation I would use interleave:
(defn my-repeat2 [s]
 (interleave s s))


@UGFL22X0Q thanks . that is a lot o f shorter code

Piotr Brzeziński13:01:22

This is how I did the same thing

(seq (reduce #(conj %1 %2 %2) [] x)))


the core library is full of gems for very specific use cases, but I’d argue it’s better to have a good grasp of map mapcat reduce since they’re much more versatile.

👍 3
Daniel Stephens13:01:29

One downside to using reduce here is that you loose any laziness, so (take 5 (my-repeat2 (range))) will never finish for example. interleave and mapcat don't have that limitation, but whether or not that's an issue is down to context!

👍 3

as always it get down to context 🙂


flatten is very brittle (breaks on common input types) and expensive (does a non-tail-recursive tree walk on input) - it's always better to use concat (or mapcat, or for) where possible


user=> (for [el [1 2 3] e [el el]] e)
(1 1 2 2 3 3)


(for is not as nice as other options here, but often fits nicely into other contexts - eg. where you are already using for and can do this in an extra binding clause)


@roelof and @peb.brzezinski when I compare my code that of more advanced Clojure developers, I too am often humbled. I’ve still much to learn, and I since I find joy in learning, especially amongst such friendly guides, I find that exciting.


@lee I agree totally with you. The community is very helpfull here

Piotr Brzeziński13:01:59

Sure thing, this is the best way. I always enjoy a good feedback/peaking at a better solution. This is one of the reasons I try to work through 4clojure and one of the reasons I’ve been going back to exercism so often 🙂


that are the two thing I do now. 4clojure for the practising and exercism for practising and the feedback of hopefully experienced developers


Hello all, does anybody have a quick reference they can point me to for setting up a networked repl connection? I'd like to remotely repl into a production app using the CLJ CLI. Thank you!


How do I make sure cloverage covers spec/keys. For some reason my test aren't covering most of them (under the forms %)


@andyfry01 socket REPL, pREPL, nREPL?

Alex Miller (Clojure team)14:01:24

there is no "socket REPL", there is a stream-based clojure.main REPL. all of these repls operate remotely over sockets.


I am not sure, but I think the phrase "socket REPL" is in fairly common usage today, isn't it? If you are trying to change that, you might want to start by calling it something else here:


I am not claiming this is "official" terminology, just commonly used terminology, e.g. it is all over the README and deps.edn file in this repo:

Alex Miller (Clojure team)17:01:28

well if you look carefully, you'll see that page says "socket server" (definitely a thing!) and "socket-based REPL"

Alex Miller (Clojure team)17:01:48

and "socket server with a repl listener"

Alex Miller (Clojure team)17:01:40

and then uses the shorter term near the end as shorthand (which I'm happy to un-shorthand if that makes it clearer)

Alex Miller (Clojure team)17:01:21

the big point really being that Clojure does not provide a socket repl, it provides a generic socket server, which can run any function, one example of which is a stream-based repl. prepl is another such example, a streaming data repl.


That all makes sense. I think developers would like a short name to call the thing they use when they use a generic socket server, which runs a stream-based repl. So far it seems like "socket REPL" is that short term used most often for that thing, as much as that name might be imprecise.


What do you experts think of my solution of the re-implmentation of range


(defn my-range [low high]
  (if (> low  high)
    (cons low (my-range (inc low) high))))


check (my-range 1 1) vs (range 1 1)



(defn my-range [low high]
  (if (>= low  high)
    (cons low (my-range (inc low) high))))

Daniel Stephens15:01:39

have you had a look at some big ranges, (take 1 (my-range 1 10000)), lazy-seq might be useful!


oke, i have to think how to use that one


is this well

(defn my-range [low high]
  (if (>= low  high)
    (cons low (my-range (inc low) high)))))


still wonders if this is good written clojure ?


Instead of if you could work with when, some people might not agree though. I prefer it because it's short


oke, I like this more. Found it on the net

(defn my-range2 [low high]
    (partial > high)
    (iterate inc low)))

Piotr Brzeziński16:01:35

One last question for today (I promise :P). I tried to implement dedupe function

(defn my-dedupe [val]
  (reduce #(if (= (last %1) %2) 
             (conj %1 %2)
             ) val))
, but when trying to execute it like (my-dedupe "aabbcc") I get an error Don't know how to create ISeq from: java.lang.Character and I’m not really sure what’s wrong here.


your anonymous function gets the first two items of val the first time it’s called

👌 3

your paren placement is super weird


try using an init for the collection rather than the implicit one (first item in (seq val) )

Piotr Brzeziński16:01:36

It was easier for me to read that way, but I get your point.


instead of (reduce f "string") , (reduce f [] "string")


@peb.brzezinski it will be easiest in the long term if we all agree on what the layout should look like - lisp conventions don't put hanging ) on its own line, especially not as a leading character before other forms


it's kind of like driving on the same side of the road - it's easier if we all stick to the agreement

Piotr Brzeziński16:01:03

Sure, I have nothing against conventions. Thanks for pointing that out.

Piotr Brzeziński16:01:08

(fn [val]
  (reduce #(if (= (last %1) %2) 
             (conj %1 %2)) [] val))
this worked indeed. Thanks once again 🙂


also, not everyone agrees here, but I prefer to align args horizontally if they are siblings - I'd write that out as

(fn [val]
  (reduce (fn [acc e]
            (if (= (last acc) e)
              (conj acc e)))

Piotr Brzeziński16:01:05

Ah ok, so each arg to reduce on the same lvl.


that makes it clearer to me - not everyone sticks to that of course


also, if a #() is complex enough for line breaks, I prefer to be able to name args


but I tend to waste more vertical space than others prefer

Piotr Brzeziński16:01:04

I prefer being explicit too in that case, I’m just trying to get used to the shorthand version of anonymous fns.


(partial reduce
         #(if (= (last %1) %2)
             (conj %1 %2))


(that's not how I'd ever write anything)


I think roughly 3 forms is a sensible limit for shorthand lambdas


I'd never specified a number myself, but that sounds about right


also I'd always replace #(do ...) with an fn that has implicit do


and I never use #(let [... ...] ...)


Both of those give me anxiety. Good suggestion.


this might just provoke a panic attack in that case

user=> (#(%2%1%):a{})


why isn’t it nil?


#(%2%1%) -> #(%2 %1 %) -> #(or (get %2 %1) %1)


it can only get that ugly because #() uses reading rules for % that don't need whitespace


ohhh I didn’t see the last %


Wait isn’t it (get {} :a :a)


right, I also expanded the get logic (not quite accurately)


Ahh right!




the third parameter of get is quite handy I think


yeah, it's actually a if-not-found not a logical or, but that's messier to illustrate

👍 6

thanks to the third parameter of get, and the leniency, you can replace any usage of get with (get get get get)


user=> (= get (get (get get get get) get get))


it's the clojure code version of that famous "chicken" talk


Doesn’t ring a bell


Thank you so much, I’m in tears, had to watch it twice. What a genius!


semantic satiation is a hell of a drug (and it's nice to avoid it in our code if we want clarity, clojure's good at that actually)

Ilan Uzan17:01:22

hi:) New here - quick question on IDEs. I've been used to working with VS code for some time now, but I understood there are much stronger IDEs for Clojure. Anyone had any experience with the VS Code extensions for Clojure and can recommend?


I use Calva for some weeks and very very happy with it

🙌 3

VS Code with Calva gives you all the essential features


The intellij + cursive option is very good too. I don’t write a ton of Java, but my experience is that if you do, you want a full-blown IDE.


Emacs is often praised. But the two times I tried I ended up in configuration hell and decision fatigue.


yeah, the emacs and vim integrations are quite featureful, but I wouldn't start learning emacs or vim just to use them with clojure


Calva is great out-of-the-box and you get auto-formatting and linting right along with it. I'd recommend that for a Clojure beginner since it does a lot for you without having to understand all the detail yet.


Oh, also Paredit :)


I'll +1 VS Code for Clojure development (although I use Clover, rather than Calva -- Clover requires a bit more knowledge of stuff, since it uses a Socket REPL instead of the more common nREPL that Leiningen starts up).


@ilan800 welcome! I'm the creator of Calva. It's made with people for whom VS Code is home turf. Please see Tao of Calva and see if it seems to chime with you.

🎉 9
Ilan Uzan18:01:22

Will take a look. Thank you!

Ilan Uzan18:01:00

Thank you all for the answers!:) I'll definitely start using Calva


Nice! Please feel welcome to #calva for assistance.


Im not really trying to change your mind, but if you want to start with a predefined emacs setup ready for clojure, I would use #practicalli spacemacs.d configuration and all instructions proposed there. It is the closest from the out of the box I know. Its emacs, out of the box and still open


Good evening, morning, night, day (depending where you live in the world!) Say I wanted add to an array this is found on a key in a map inside of a vector atom, how would I do that? Simple example: (def todos atom [{:id "1337 :desc "todo-list" :todos []} ]) How would I add todos this atom?


@georghagen (swap! todos conj ...)


@georghagen an atom is a container that manages the changes to its contents, the basic things needed are swap! and deref


it looks like your atom has a weird structure, which means you would need to use a more complex call to swap! or use a simpler structure


Any suggestions for a simpler structure?


for example

(ins)user=>  (def todos (atom []))
(ins)user=> (swap! todos conj {:what "homework" :when 0})
[{:what "homework", :when 0}]


currently your "todos" seems to contain metadata that isn't the contents, but about the contents


In JS I would just do todos[0].todos.push({id: 2, desc: "okdf"}). Yeah maybe I should just show you my real example


(defn create-checklist [] { :id (.toString (java.util.UUID/randomUUID)) :sequenceMap [] ;Map<Integer, UUID> / Map of question number vs question UUID, to create sequences for each checklist / :answerMap [] ;Map<Integer, UUID> :expectedAnswerMap [] ;Map<Integer, UUID> :isSigned false })


[] is never a map in clojure


Im using a template from a kotlin programmer


yeah, that doesn't really look like clojure code (though it would compile)


you can combine swap! with update-in to do nested operations

👍 3

ins)user=>  (def todos (atom {:id 0 :items []})) 
(ins)user=> (swap! todos update-in [:items] conj {:what "homework" :when 0})
{:id 0, :items [{:what "homework", :when 0}]}


this is closer to what you initially had


but using an atom and def already implies a global / singular nature that doesn't line up with the particularity of having and id etc. at the top level


Aha! I see.. I am using to simulate a database


because I am creating mock data for a frontend


right, that's the usual role of an atom


on reflection my first example is closer to what you had, I misread it


having a vector at the top level of an atom is relatively rare, usually you want keyed rather than indexed lookup


but perhaps your db is actually a queue or stack


Nono, it was a choice made without too much thought. I am coming straigth from javascript


I have not chosen a db yet actually


right, instead of [{:id 10 ... ...} {:id 20 ... ...}] we'd usually have {10 {... ...} 20 {... ...}}


(or in your case actually the uuids you generate)


Yeah of course, makes much more sense to have the UUID as a key to your other checklist data


unlike js we only sometimes use hashes as "objects", and generally use them as databases


so more like

(ins)user=>  (def todos (atom {})) 
(ins)user=> (swap! todos assoc (java.util.UUID/randomUUID) {:what "homework"})
{#uuid "701b9c5d-6d61-45e6-bf81-8024953aa50d" {:what "homework"}}


@georghagen a pattern you'll likely see is that most of the sort of code you'd usually write is replaced with a few combinations of collection operations in clojure code


Yeah, that's what I really love about Clojure. I just have to get more experience to know my way around the collection operations! Thanks a lot for all the input, can't wait untill I can start contributing back to the clojure community myself 😃

🎉 3

GN my friends. Hopefully I can work on exercism or 4clojure or my web project tomorrow

👋 9

IS there a good free hosting platform for the website im building with compujure and ring ?


There are cloud providers who offer free tiers. The JVM is often supported. A simple provider would be Heroku, but bigger (and more complicated ones) like AWS, Azure and GCP provide free tiers as well as far as I know.


I don’t vouch for any of these, but it might be worth checking them out.


You could try this: You'd need to switch to I think. And I've never tried them.


But generally, people use VPS hosting, which I do not know of any free options


DigitalOcean, Linode, AWS, are popular options, they have things in the 5$ a month range.


I think Heroku might have a free option for MVPs and personal projects, not sure


thanks all


@georghagen You should maybe consider looking into the set data-structure and relational operations. Your collection looks like a set (since it has unique ids). You can use index to get a map from a unique key to each item in the collection, you can join sets arbitrarily, select on a predicate and so on.


to me it doesn't look like a set - sets are about things that only stand for themselves and comparison of membership, this looks more like a mapping to me


most of the data you'd put in a "todo list" entry doesn't stand for itself, and most todo list operations are not set operations


Ah right its a todo list. A simple map makes more sense there probably; KISS.


I’m currently diving deeper into logic (math and programming) and everything looks like a set to me…


I think I confused you with the todolist example. Its a checklist, which will have questions attaches to it. these questions will have their own id and description and an id which denotes their relation to an answer


is it an ordered thing for display?


there are specialized data structures for that


I think thats the only thought behind having an integer tied to an UUID, to make sure everything is in the right sequence. But that could be implied in the datastructure. But if the client wants to be able to move it around, you might be in trouble? I don't know


yeah, so the best idea is probably to separate the UI layer (stuff about ordering and labeling) from the relational layer (links between questions and answers) and the storage layer (indexing by id)


clojure solutions to this sort of thing will definitely look unfamiliar coming from kotlin or js, and will often be specific to a particular UI tool (eg. reagent / re-frame)


the frontend is being build with ionic in react


I am creating the backend as a rest api


OK, in that case we should probably go back to vector-of-maps (if there's only one global todo), or a map from session / user id to vector of maps


and that's either in an atom (in memory) or an intermediate structure used when talking to a db


That's right!

Alex Miller (Clojure team)20:01:48

If you are new to Clojure (or not!), we would love to have your feedback in the annual State of Clojure survey - we've been doing this survey in the community for about a decade and it provides important information about trends and usage. If you're interested, last year's results can be found here:

🏁 12
metal 3
👍 3

Did already a few days ago


The questions ask whether you use it professionally, and then veer off and assume you do

Alex Miller (Clojure team)03:01:05

as it says at the top, just skip anything that doesn't apply

Alex Miller (Clojure team)03:01:11

only the first 2 questions are required


I'm having trouble finding the clojure equivalent of lein deps, has anyone got it to hand?


i'm dockerizing something and the recommendation from the clojure image is to download deps ahead of time, but the examples are lein-based


You want an option to clojure that will only try to load dependencies, and then stop?


clojure -Spath should do the trick


A reasonably common way people deploy Clojure-based apps that avoids this question entirely is to create a JAR file that contains all of the code of your app and all of its dependencies, and then simply run java -cp ... command on the system where the application runs. Then that system doesn't need to be able to access repositories containing libraries at all.


agreed with @U0CMVHBL2 that a jar is the simplest thing in the long run, depstar does this task well with the clojure tool


@U0E9KE222 If you're using an up-to-date clojure CLI ( or later), you can use the -P option. See


That will just "prepare" things, i.e., download the dependencies you need and then not run anything.

Alex Miller (Clojure team)03:01:10

-P is the preferred way to do this


thanks folks!