Fork me on GitHub

hey team another beginner question. i have two tables right now user and address . the address table relates to the user table. in java/spring world when i query the database i get back a user object that has a nested address object in it

  address: { ... }
when i do this in clojure the map returned is merged together so i get 1 object back with all the data at the top level. how would i do this in clojure? i am using next.jdbc


perhaps this is wrong to think about?


OR would these be separate database calls and i construct the map myself?


@tjb result sets are flat -- by default, next.jdbc returns qualified keys in results so you'd have :user/id, :user/col, :address/id, :address/street etc. That's idiomatic in Clojure, to have a flat hash map with qualified keys.


gotcha so it is on me to construct the map then correct?


Not sure what "map" you mean. next.jdbc already gives you a hash map.


sorry let me example


so i get something like this (all dummy data so IDs are ok to share here)

{"address/id":"2b6d3a10-acf5-4142-a7cd-2b0f83a85b37","address/contact_number":"","app_user/first":"Yusuke","address/user_id":"c0c03eff-9870-4c0e-83ac-506bf59d69cf","address/country":"USA","app_user/email":"","app_user/id":"c0c03eff-9870-4c0e-83ac-506bf59d69cf","address/state_region":"IL","app_user/last":"Urameshi","address/postal_code":"60622","address/extended_street_address":"Suite 420","address/street_address":"123 Money St.","address/city":"Chicago"}


which is a flat map


however i was going to hope i could get this to return like

  first: ""
  last: ""
  address: {
    id: ""


does that make sense?


maybe im too use to ORMs doing this for me


Keys are strings? Oh, you're converting to JSON?


this is the response from my controller


So you want to convert the idiomatic internal Clojure format into a specific nested format to return to your clients?




and im mostly jsut trying to see if i need to roll this on my own, which im ok with just want to make sure


It's just Clojure data. Transform it however you need to get the format that you want the API to return.


ok that makes sense


(I would not expect the API format to match the database format anyway)

👍 4

is this normal in professional clojure dev too


to manipulate the database response into whatever format and then return that to the client?


since everything in clojure is Just Data™


I'd say so -- your database format is going to be optimized for storage/queries; your API input/output is going to be optimized for clients. Those won't necessarily be the same (but might be in some cases).

👍 4

this is very interesting


from my experience in the java world your domain models mimic how your database is set up


thank you again for the insight @seancorfield!


Yeah, that's why ORMs are so terrible.


i def can start to see pros/cons to both


pros to ORMs -- less coding


cons: very rigid


pros to no ORM: flexibility


cons: i have to write more code 😛


I've used a wide range of ORMs over a couple of decades and I've hated every single one 🙂


bookmarked for later!


im going to toy around with defrecord as that may help with what i need


but i think i just need to write a custom mapper to map the db response into the shape i want


No. Resist. You don't need records. Unless you want type-based dispatch.


i will resist then 🙂 i probably do not need it


ill go with the custom mapper option


If you want to collect out attributes based on the qualifier, you can call namespace on the keyword column name to get just the qualifier as a string... that might help automate it...


hmmm do you have an example by chance?


(reduce-kv (fn [m k v] (assoc-in m [(namespace k) (name k)] v)) {} results)

👍 4

That will give you a map where the top level keys are the qualifiers and each one has a map of the unqualified keys that belong to it.


results would be the data returned from the DB correct?


Would be each row, yes.


So then you could do (assoc (:user data) :address (:address data)) to get your nested map.


oh wow holy cow


this is amazing


giving this a go right now


If you had a bunch of things joined and thus multiple nested things, you could do (merge (dissoc data :user) (:user data))


bah! this is a tangential question: why are my keys from my db strings and not :address . when i call my database i get "address":


can they be converted somehow?


next.jdbc returns them as keywords.


wait im sorry this is what prints


{address {user_id #uuid "c0c03eff-9870-4c0e-83ac-506bf59d69cf", country USA, contact_number +, extended_street_address Suite 420, city Chicago, postal_code 60622, id #uuid "2b6d3a10-acf5-4142-a7cd-2b0f83a85b37", street_address 123 Money St., state_region IL}, app_user {first Yusuke, email , id #uuid "c0c03eff-9870-4c0e-83ac-506bf59d69cf", last Urameshi}}


from the database using your reduce-kv


how would i get them to be :address unless this is the same thing?


Looks like you handed the JSON to the reduce-kv instead of the database result?


(defn get-user
  "Get user by id"
  [id req]
  (let [db (-> req :database)
        uuid (string-utils/to-uuid id)
        data (reduce-kv (fn [m k v] (assoc-in m [(namespace k) (name k)] v)) {} (first (repo/get-user db uuid)))]
    (println data)
    (assoc (:app_user data) :address (:address data))))


repo/get-user is


(defn get-user
  "Get user by id"
  [db id]
  (sql/query db 
             ["SELECT *
                FROM app_user au 
                INNER JOIN address add ON = add.user_id 
                WHERE = ?" id]))


Oh... sorry, my code was a bit too off-the-cuff. Yes, namespace and name produce strings. So the DB result was with keywords but my reduce-kv turned them into strings.


ah! ok ok ok


> So then you could do `(assoc (data "user") "address" (data "address"))` to get your nested map. ^ that will give you almost your JSON


Or (reduce-kv (fn [m k v] (assoc-in m [(keyword (namespace k)) (keyword (name k))] v)) {} results)


That will produce the keyword-based versions instead of strings.


(that's what I get from coding in my head instead of in the REPL 🙂 )


ok very cool!


now i have two nested objects


{"address":{"postal_code":"60622","extended_street_address":"Suite 420","city":"Chicago","contact_number":"+","state_region":"IL","id":"2b6d3a10-acf5-4142-a7cd-2b0f83a85b37","street_address":"123 Money St.","user_id":"c0c03eff-9870-4c0e-83ac-506bf59d69cf","country":"USA"},"app_user":{"first":"Yusuke","email":"","id":"c0c03eff-9870-4c0e-83ac-506bf59d69cf","last":"Urameshi"}}


The nice things about Clojure's "data first" approach is that you can slice and dice this any way you want.


allllmost there


(defn get-user
  "Get user by id"
  [id req]
  (let [db (-> req :database)
        uuid (string-utils/to-uuid id)
        data (reduce-kv (fn [m k v] (assoc-in m [(keyword (namespace k)) (keyword (name k))] v)) {} (first (repo/get-user db uuid)))]
    (println data)
    (set/rename (assoc (get data :app_user) :address (get data :address)) {:app_user :user})))


(defn get-user
  "Get user by id"
  [id req]
  (let [db (-> req :database)
        uuid (string-utils/to-uuid id)
        data (reduce-kv (fn [m k v] (assoc-in m [(keyword (namespace k)) (keyword (name k))] v)) {} (first (repo/get-user db uuid)))]
    (set/rename-keys (assoc (get data :app_user) :address (get data :address)) {:app_user :user})))


gives me

{"first":"Yusuke","email":"","id":"c0c03eff-9870-4c0e-83ac-506bf59d69cf","last":"Urameshi","address":{"postal_code":"60622","extended_street_address":"Suite 420","city":"Chicago","contact_number":"+","state_region":"IL","id":"2b6d3a10-acf5-4142-a7cd-2b0f83a85b37","street_address":"123 Money St.","user_id":"c0c03eff-9870-4c0e-83ac-506bf59d69cf","country":"USA"}}


which is what i was looking for 🙂


lets gooooo!!! thank you again Sean!!


Why the rename? You don't have :app_user in that hash map.


Also, you can use (:app_user data) and (:address data) -- that's more idiomatic when you have known keywords @tjb


i do unfortunately due to postgres




No, (:app_user data) won't contain that key.


And then you assoc in :address


(assoc (:app_user data) :address (:address data))


wont this put :address into :app_user?


(set/rename-keys (assoc (:app_user data) :address (:address data)) {:app_user :user}) to rename :app_user to :user


data is {:app_user stuff1 :address stuff2}


so (assoc (:app_user data) :address (:address data)) will give you stuff1 with a new key :address whose value is stuff2


right and im renaming {:app_user { :address { ... } } to {:user { :address { ... } }


after that transformation


But it's not in that data.


Look at your result -- it doesn't contain user.


There's no :app_user in that structure.


oh i see what you are saying


yes but this is ideal since i want the top level object to be the user


:app_user is in data, but that's not what you're assoc-ing into.


{ :first "" :last "" :address { ... } } is ok


And that's what you'll have -- without the rename


good catch thank you sean


i have a final question for the channel


has anyone encountered having to convert kebab case json to camelCase for their client?


lol channel = sean

💯 4

And you Dan 🙂


lolol i def dont wanan bug sean and want to ask everyone


(right now at least)


although sean is answering 🙂


There's a great library called camel-snake-kebab for converting names.


i saw that lib was am confused where it would live


ideally i dont want to put that on each function i create


At work, our JS client team use snake_case for interop with our Clojure backend but I think they also transform into (different) data structures with camelCase


i currently have a function that wraps responses like this


(defn jsonify-routes
  (-> routes
      (wrap-cors :access-control-allow-origin [#""]
                 :access-control-allow-methods [:get :put :post :delete])))


yeah. there's some pain of json versus postgres versus edn types. postgres doesn't like hyphens or question marks. edn needs to become json in the db but can become edn again coming out. we tuck these thing inside a db layer and don't see them and then just declare some stuff


Middleware could automatically transform all inbound and outbound keys for you...


and then deciding if things will be strings or keywords. can get a bit dicey


so at the DB repo layer you are doing the conversion?


right before going in and right after coming out yes


when the data comes out of the database


does that mean you have to wrap each function you create to convert that data?


Our DB layer is (mostly) snake_case these days (we started off with camelCase but quickly decided that was a bad idea! 🙂 but of course we still have legacy tables with camelCase names).


do yall tack on question marks to question-y columns? :state "cancelled" type things, do those become keywords?


I try to avoid ? on keywords.


ah right, you prefer that for predicate functions right


(sorry didn't mean to hijack your question @tjb)


I'm on the fence about ? on local symbols. Some days I feel more favorable to it than others 🙂


i am listening in and learning 🙂


Re: DB layer and domain layer naming -- we've gone back and forth on that over the years. For a while I was in favor of using :identifiers (c.j.j) and builder adapters (next.jdbc) to perform automatic conversion coming out of the DB (and :entities / :column-fn for going into the DB).


But now I'm more inclined to let :snake_case leak out of the DB layer and into the surrounding code...


It's fewer moving parts at the expense of some slightly less idiomatic keyword names.


woot! got some middleware workin


for the curious it looks like this


(defn wrap-json-to-camel-case 
     (let [response (handler request)]
       (assoc response :body (cske/transform-keys csk/->camelCaseString (:body response)))))))

(defn jsonify-routes
  (-> routes
      (wrap-cors :access-control-allow-origin [#""]
                 :access-control-allow-methods [:get :put :post :delete])))


i was looking at ring source code so i dont fully understand stuff yet but it works...


it seems to me, (def a (atom some-non-persistent-java-container)) is totally meaningless.


atom is only valid for persistent data structures.


swap! on an atom uses a JVM method that effectively uses identical? to check whether the item is equal or not equal, so a JVM mutable collection that remained identical? across mutation operations inside of a swap! operation would be buggy.


The intended use case is to hold immutable values inside of atoms, and most Clojure mutable reference types.


@tjb Glad you got it working! You could do (update response :body #(cske/transform-keys csk/->camelCaseString %)) instead of that assoc if you find that reads easier.


And of course now you have middleware, you could auto-convert your input parameters (:params request) to kebab-case as well.

‼️ 4
Umar Daraz06:04:52

Hello clojurians, how to store integrant system for later access in repl? I want to call my reitit routes from repl after initialing integrate system.

;initializing handler key like this
(defmethod ig/init-key :shop/handler [_ {:keys [db] }]
  (router/create-app db))
; and initializing system 
(defn -main []
  (println "Running main!")
  (ig/init system-config))


i.e. integrant.repl.state/system

Umar Daraz08:04:49

Thanks @UJF10JP8A I tried to add integrant repl and start/stop is working. but did not knew that we can get system of repl/state. thank you!

👍 4

so, I am really in trouble with this pretty print because at the moment I do not have a week to spend just reading up on all the possible variants, I would just like to see an example tool or even example usage of the built-in pprint that prints formatted values, or at least indented and newlined values 🙂


right now all I get is a big line


I don't know where to look for the bits to tweak, and how to turn and nib at them to do what I wish it for to do

Chris McCormick09:04:48

@ashnur are you developing in the browser?


yup, shadow-cljs


I know I could do clj->js and use JSON.stringify but I would like a less indirect solution 😄

Chris McCormick09:04:31

you could also try (js/console.log (clj->js val)) and then take a look in the browser console


I guess my previous line is ignored then?

Aron09:04:31 I tried to use this but it made things worse, not better, it added a bunch of newlines but nothing else

Aron10:04:48 this works more or less, although it was a bit of a struggle still 🙂


is it the case that you would rather not look at the browser console?

Chris McCormick10:04:22

> and use JSON.stringify @ashnur i mean use the console without doing this


i ask because i did get some use out of: -- it just requires looking at the browser console

😍 4
☝️ 4

i guess it implies a certain browser-type as well


there's also a hard-core version by the same author (


the latter can be a bit much to set up and keep up-to-date iirc though


@chris358 you are ignoring all what I wrote apart from some bits and pieces 🙂 but thanks nevertheless for attempting to help and I would like to reiterate that the last link has solved my question, it works and I am now trying to do something else


good to hear you found something that works -- it wasn't clear to me from your "more or less"


thanks @sogaiu will try those too, but yeah at the moment I just wanted inline in the browser window


good luck 🙂


well, it's not quite what the screenshots are displaying, but it gets the job done

Jose Ferreira15:04:00

Hey there, i've been looking around for a clojure book to buy, so that i can start learning clojure and clojure script, what would you guys recommend?

Jose Ferreira15:04:59

Everything i found seems kinda outdated, books from 5 to 10 years ago. Would they still be relevant today?


For “getting started stuff,” Clojure is pretty stable over the 5-10 year time frame, so you can probably get a good start even with the older book.


Clojure for the Brave and True is frequently recommended for beginning Clojure


And there’s also a #braveandtrue channel if you have questions

Jose Ferreira15:04:06

Is that also true for the clojure script enviroment?

Jose Ferreira15:04:34

I've been through most of brave and throught and Programming Clojure

Jose Ferreira15:04:09

I was looking more for a book that could give me an overview of how to really build something


A lot of Clojure stuff transfers over cleanly to cljs, but there’s a few differences due to the underlying language being js rather than java

Jose Ferreira15:04:06

when i say most, that might be an overstatement, (i finished the basic stuff)


Once you have the basics down and are comfortable solving small problems/exercises, it starts to get to be more a matter of putting together libraries and building applications out of that

Jose Ferreira15:04:27

Exactly, but what would be good resources for finding the right tools? How to i build the dev stack?

Jose Ferreira15:04:02

All of this seems very different from what i'm used to...:(

Jose Ferreira15:04:29

There's no framework that gets everything you need right?

Jose Ferreira15:04:41

Something like django or laravel


Yeah clojure is more library oriented than framework oriented. There’s luminus, which is kinda framework-y ( Luminus basically puts together a bunch of libraries for you, but that can be overwhelming for beginners because there’s so much to learn at once


A lot of people recommend starting with just a simple ring/compojure server, and adding each library as you need it, so you can absorb each library one at a time.


You might also want to look at Sean Corfeld’s sample library at

Jose Ferreira15:04:53

tks a lot, i'll be looking into that

Jose Ferreira15:04:42

but just to clarify, if i get a cljs book from 2012, the tools that are used in the book would still be relevant today?

Jose Ferreira15:04:51

i was looking at web dev with clojure


I’d say still useful from a learning perspective, maybe do a test/sample project with it and then once you’re familiar with the basic libraries, you can start looking at what other libraries might be good. For example, Compojure is probably the most widely used library for basic routing, but there’s a new library called reitit that I really like (though it’s still maturing). So compojure is good to learn and you’ll get lots of help with it, and once you know it you might want to check out reitit or one of the other routing alternatives.

Jose Ferreira15:04:20

OK, i'll try to build something basic with compojure and ring, and see how that goes...^^

Jose Ferreira15:04:48

but what would be the big difference between compojure and reitit?

Jose Ferreira15:04:09

Is it that one would be better in a specific situation?


reitit describes routes as data. Compojure describes routes via code (macros). I think reitit supports bidirectional routing but Compojure does not.


@jose.ferreira.ptm2 You asked about a "cljs book from 2012" -- ClojureScript, specifically? I believe the recommended tooling for ClojureScript has changed dramatically since then and I think macro-handling in cljs has changed too... but I don't actively use ClojureScript (just Clojure) so I can't be sure of that (there's a #clojurescript channel where you can probably get more complex/accurate cljs questions answered)

Jose Ferreira16:04:49

tks a lot sean, thats what i feared...

Jose Ferreira16:04:38

im trying something simple for myself now, using just clojure with ring and compojure as was recommended by @manutter51

Jose Ferreira16:04:12

I'll decide wich book to buy when i get stuck with something and feel that i need some more structure to learning this stuff

Jose Ferreira16:04:21

I think i was looking at this all wrong. I'm used to big frameworks that do a lot of the big structuring work for you

Jose Ferreira16:04:27

and are very oppinated...

Jose Ferreira16:04:38

i guess thats just not the clojure way


There is a recent 3rd edition of web dev for Clojure, much more recent than 2012.


That 3rd Ed is in progress but is definitely getting overhauled content in the cljs stuff (I bought it in "prerelease" form and get updates as new sections as written).

Jose Ferreira16:04:15

it seems its not out yet thou


You can buy the preview/beta ebook and get updates as they come out.


(that's what I did)

Jose Ferreira16:04:34

i'm old school in that regard, i like to have my books in physical form

Jose Ferreira16:04:06

Do you recommended it?


Last update was March 25th. Beta 7 of the ebook.

Jose Ferreira16:04:16

if you've read it already...


It's a good book (3rd ed -- 2nd ed is outdated) but I'm not a fan of Luminus really. It makes a number of choices I don't like.


But, for learning web dev with Clojure, it's probably the best option. Be aware that the first chapter or two are really overwhelming because they throw all of Luminus at you, then later chapters go back to basics and walk you through most everything from the ground up. I think that's a bad idea -- I'd prefer to see Luminus only introduced once the basics are already covered -- but, hey, I'm not the author and he's the one who wrote/maintains Luminus too so it's not surprising it's featured upfront in his book 🙂

Jose Ferreira16:04:29

shameless plug....xD

Jose Ferreira16:04:07

but yes, i guess i understand the you the cappabilities, and then explain it in more detail

Jose Ferreira16:04:15

top down aproach


My recommendation, much like @manutter51’s, is to start with the basics: Ring, Compojure, then maybe Selmer for web pages, and Component for managing the lifecycle of things (like web servers and database setup), and get comfortable with that on the server first. Then start in on ClojureScript using the official docs and recommended tooling and, again, get through the basics first. Then look at combining them into a full-stack app.

Jose Ferreira16:04:57

ok, selmer seems pretty straightforward, just a template system....

Jose Ferreira16:04:11

component seems like a more alien concept...


What kind of thing are you trying to build? (=

Jose Ferreira16:04:40

nothing in particular right now...just dabbling

Jose Ferreira16:04:57

i saw a few talks about this and got curious

Jose Ferreira16:04:03

just managed to say hello world to the browser,

Jose Ferreira16:04:42

lets hope it gets a little bit more exciting than this


Yeah, there are a few different projects for the web-based project, but you must be comfortable with compojure and ring. getting good with those for GET/POST actions will make things easier later on. like if you're doing authentication for users logged in or not you would do it on the layer of ring/compojure then when you're serving html, you can use different templating libraries like selmer ... then when you want to go more elaborate on the clientside you can use figwheel to start a mini clojurescript prototype, and when you are comfortable with clojurescript you can use it to make react components using Rum and if you need to make a real-time webapp with responsiveness between server and client you can use Sente as a starter project which has both server and client starting points for you.


Fulcro is also a big solution for the big problem of web applications with a serverside and responsive clientside based in js, i would recommend looking at it if you need a robust solution for something gigantic one day. There are many useful concepts in all of the suites but it's far easier to do software maintenance longterm when you pick and choose only the components you need.

Jose Ferreira16:04:00

i like how everything is very modular around here...


I would hang my stomach up on the wall while not using it if I could xD

Jose Ferreira16:04:46

i just find that that are a lot o tools that do basically the same thing, (or i'm just not understanding the differences between them)


Hey @jose.ferreira.ptm2 I’m working on CS50 Project1 ( ) but in Clojure right now, and learning clojure on the process. If you want to take a look this is the link


Yeah it's hard to call it redundancy per se because a lot of projects do things in a slightly different style but achieve the same result. Basically, many projects are black boxes (you can always examine the source code so .. transparent boxes?) that are made to achieve some sort of output for some given input and the implementation is ... 😄 whatever it needs to be. Oftentimes if one is not serving you, you can drop in another library with little or no editing

Mario C.16:04:43

I want to map through a collection and in the mapping function I would like to have access to the rest of the collection, ie: the whole collection minus the current element. I didn't find anything that does exactly that so I created a new function map-with-group (Not fond of the name) that does this. I was wondering if this is the right approach. Or if it is too much work that can be easily expressed within a regular map or other constructs. Here is the function:

(defn map-with-group
  "maps through the given collection while holding a list of
     all the elements in the collection excluding the current element in the mapping.
     f: Is a function that recieves two arguments
         1. The current element.
         2. The collection minus the current element.
     coll: The collection."
  [f coll]
  (let [group (apply assoc {} (interleave (range (count coll)) coll))]
    (map #(f (second %) (vals (dissoc group (first %)))) group)))

Jose Ferreira16:04:36

@orlandomr27 {:status 200 :body "Olha a cara do bis!" :headers {}})


the register-and-login branch has more features. I’m working on register form

Jose Ferreira17:04:14

yeah, i'm looking through

Jose Ferreira17:04:20

So this is basically a book DB right?

Jose Ferreira17:04:51

what are you using as your learning resources?


a lot of things but mainly Adam Bard’s blog and video about web app in Clojure. and LambdaIsland videos


take a look at project1 requeriments and you are invited to PR on my project if you want. We can learn together in the process of building this app

Jose Ferreira17:04:12

do you have a link for the project?


I’m confortable with spanish and portuguese yes :)

Jose Ferreira16:04:34

@sova yes, but the entry point for someone who starts looking into this seems very convuluted


I guess this is part of the process for clojure maturing curve. Considering that clojure is a relativaly new lang. Some libs will just endure and some not until the whole ecosystem reach a point that for example python has grown.

Jose Ferreira18:04:54

yes, but clojure is not really a new language...

Jose Ferreira18:04:11

neither is LISP, and this has been a problem for all LISPS


Clojure is designed by/for experienced developers so on-boarding beginners has never really been a focus. The beginner experience has gotten a lot better over the years but it will always have a steep on ramp for beginners because of its focus and philosophy: composition over libraries over frameworks, abstractions over concretion, etc. The analogy is often made to a violin -- in expert hands it is beautiful but a violin is extremely difficult for a beginner to start making music with.

clj 8

In almost every problem space, Clojure has multiple solutions that each make different trade offs (e.g., Mount vs Component vs Integrant vs ... -- just for managing state and start/stop lifecycles)

👍 4
Jose Ferreira17:04:20

ok, but if you want mass adoption of a programming language, that doesn't seem like a very good strategy


Mass adoption is not a goal for Clojure.

😄 4
Jose Ferreira17:04:53

why not?

😅 4
Jose Ferreira17:04:22

wouldn't it be beneficial?


See Stuart Halloway's tweets like this (he and Rich are the primary stewards of Clojure)


I'm curious as to what you think would be beneficial about mass adoption?

Jose Ferreira17:04:01

well, a bigger community would mean more people contributing

Jose Ferreira17:04:26

isn't that what makes languages flourish?


You were just complaining about how many libraries you already have to choose from 🙂 And contributions to Clojure are very strictly vetted so more people isn't going to make Clojure itself evolve any faster.

👍 4
Jose Ferreira17:04:35

yeah you're right about that one

Jose Ferreira17:04:54

seems like a small but very productive community

Jose Ferreira17:04:34

but mass adoption would also bring more attention, and there would be more jobs


Many Clojure libraries are very small and focused on solving specific problems -- which means they are often "complete" fairly quickly and then don't need much maintenance going forward.

👍 4
Jose Ferreira17:04:17

Lisps have been failing to take over the world continusly for decades now

😆 4
🎯 4

Most Clojure shops are very small -- because a few people can get so much done so quickly with the language. There are some places that have huge teams -- NuBank in S. America has hundreds of Clojure developers -- but those are rare indeed. Lisps are just seen as too "weird" by most mainstream developers 😐

Jose Ferreira17:04:39

but the concepts are awsome...

Jose Ferreira17:04:11

i belive if the entry point wasn't so hard, there would be a lot more people ready to take it

Jose Ferreira17:04:54

and by that, i don't mean lang syntax or semantics

Jose Ferreira17:04:15

i just mean organized content

Jose Ferreira17:04:51

JS is very convuluted also, you have hundreds of libs and frameworks as well

Jose Ferreira17:04:09

but you can find a lot of content, and a lot of guides


And a lot of that JS content is quickly outdated because of all the (backward-incompatible) churn in libraries and frameworks etc 🙂


I've been building software for about 35 years and I find the JS ecosystem to be absolutely bewildering and extremely hard to navigate. Libraries and frameworks continually change and break things. There is "too much" documentation so finding good, up-to-date material is harder and books are out of date just months after their release. Even the language itself is changing constantly so "best practices" are outdated within months sometimes and the tooling is really brittle and really complex. At work, our front end is built is JS, with React/Redux/etc, and watching our front end team wrestle with build and test tools, and performance, and all sorts of weirdness in browser and device support... I have huge respect for their ability to get anything done!


I find Clojure's ecosystem to be a model of simplicity and stability by contrast.


Occasionally I have to write a bit of JS and every time it is an exercise in frustration for me!


The on-ramp with JS is very gentle tho'. You can hack stuff together quickly because it's just like all the other C-family languages out there. Sure the prototype OO stuff is a bit weird but it's all based on mutable, imperative practices that are fundamental to almost every other language. The on-ramp with Clojure is very steep because it focuses on immutability and abstractions -- you can't avoid any of that stuff, and pretty much everything you know from "regular" languages has to go out the window (and the more steeped in OOP/mutability you are, the harder Clojure can be to learn).


So, yes, Clojure is hard to learn. I think it's easier if you've never programmed at all than if you have "only" experienced either JS or Java.


(which is all my way of saying "don't get discouraged -- this is hard and we all know and accept that" 🙂 )

Jose Ferreira17:04:40

i totally agree with you, but you're missing my point (or i wasn't able to get it across correctly)

Jose Ferreira17:04:38

it's not the syntax or the philosophy of the language that i'm "complaining" about

Jose Ferreira17:04:06

i get that it's different, i had a few prior experiences with lisps before

Jose Ferreira17:04:52

its just that there are very few beginner content...


You say that, but having been hanging around helping beginners for a while, I think the issue IS having to learn unfamiliar concepts

Jose Ferreira18:04:17

you have a lot more experience than me on that regard then, but if the begginer content was more widly available and digestible, all that unfamiliarity would be a lot easiear to handle


Well, what I see is people get the impression that their struggle is due to the lack of "easy to digest" tutorial and guides


But it's not that their are no guides or tutorials for everything, it's that none of them are "easy to digest" because the thing their trying to explain can't be reduced to a few sentences and a comparison with JS


So really, it's that the material isn't "easy to digest" and so it can't be explained in a "easy to digest" manner either


And I'm not just talking about the concepts of say how higher order functions work. I'm talking also about the pragmatic concepts like how the JVM works, how the Clojure evaluation model works, how the interop works, how the language is bootsrapped, how dependencies work, how compilation works, how the REPL works, etc.


Though I'll grant you that there's not a lot of material and documentation around common frameworks. Like how ring works, how compojure works, how reagent works, etc. A bigger community would probably help here.


That said, and this is me ranting a bit now, I feel often people coming from web dev backgrounds arn't really trying to learn the language, but are only trying to learn the frameworks. And so they start complaining there's no material to teach you the framework, there's not even a clear choice of framework, and that it seems that most framework lack all the features they'd need, etc.


And that's all true, those things arn't there, and if they were they could happily avoid learning the language, and just navigate it at the surface, leveraging frameworks and framework based guides and tutorials


And the answer they normally get is, once you learn the language proper, you won't care for a framework or those guides, you'll just know how to put together your own little framework and be done with it.


Which is... A lot to ask, like this is part of the "steep learning curve". So you're back at having to learn a lot of unfamiliar things.


But I don't have any solutions to this, because that's truly what people who use Clojure[Script] actually do 😝. They each setup their own preferred stack with lots of custom in-house bits and pieces. They don't use this common framework which has a dedicated team behind it where all simply make use of it and wait for it to have new features.

Jose Ferreira19:04:35

yeah, i get what you-re saying

Jose Ferreira19:04:06

i was also comming to clojure with the wrong mindset, looking for a structered framework

Jose Ferreira19:04:15

where everything was setup for me

Jose Ferreira19:04:35

i-m starting to get that the environment itself is very different

👍 4
Jose Ferreira19:04:09

i'll tackle on, eventualy all those little things will become second nature

❤️ 4
Jose Ferreira19:04:48

the truth is, this is harder, but its also better. it forces you to learn about all the little things

Jose Ferreira19:04:06

that are done automaggicallyby framworks like django or laravel


And I don't know if we should have a nice full featured framework or not. I think people who know Clojure well manage without it, and don't feel enough pain from that to be motivated in contributing to build a big common framework. So like, maybe it be nice if we did, even for me, maybe I don't think I need one but would still benefit a little from one. And for sure it would make Clojure more approachable to some people. But reality is there isn't, and no one seems motivated to build it. And when some tried, nobody got on-board, the community didn't all homogenize behind any one framework.

Jose Ferreira19:04:08

yes, it might not even be needed... but that is the norm form people comming from other paradigms

Jose Ferreira19:04:18

so thats the first thing they'll be looking for


So I just mean, as someone learning, you need to know that things are different here, and that means lots to learn and re-learn. But ya, I think it is worth the investment obviously. But if you're looking for things to be like elsewhere, you'll just be frustrated. Go to accept it isn't. :man-shrugging:

Jose Ferreira19:04:36

I just think that needs to be explicit

Jose Ferreira19:04:24

exactly as you were saying his a matter of expectation


Ya me 2 haha. I try to be upfront with people. But I don't know, maybe should have a guide saying this, but sometimes people don't read everything on either hehe


And we don't want to send the wrong message either

Jose Ferreira17:04:14

in languages that have a more wide adoption, you can almost get carried on the lap by all the content you can find to get you sarted

Jose Ferreira17:04:35

There are very concise guides and explanations


The official Clojure Getting Started guide -- and Alex and the team are often open to additions and enhancements there.


(I'm not trying to be dismissive of the problems for beginners -- having the official getting started / learn Clojure / programming at the REPL material enhanced is going to be the best way to help smooth that on-ramp -- but that feedback pretty much has to come from beginners or near beginners because they know best what changes are needed and what material is missing)


For copyright reasons, contributors to the site still need to e-sign a Contributor Agreement per

Jose Ferreira17:04:40

yeah, you're right, i guess i was just a bit frustrated for trying to figure this stuff out for 2 days...:)

Jose Ferreira17:04:42

I'll keep hacking on, and when i get this stuff out maybe i'll try to add my 2cents to help others start

Patrick Truong18:04:44

@jose.ferreira.ptm2 as a fellow beginner picking this up right now I can empathize with. There are just a lot of different ways to configure things that achieve the same result. I was finding the combination of lein + shadow-cljs vs figwheel vs lein alone very disorienting (I believe you were working on ClojureScript?) along with just learning the nuances of the language + reagent, etc. I get the sentiment of the community not necessarily being geared for beginners, but I don’t think it takes away from the amazing work in the community to continue building more beginner friendly walkthroughs/guides and making Clojure(Script) more approachable to beginners. I don’t think anyone is actively preventing that, probably the experienced people haven’t felt that pain point in a long term or at all. I definitely plan to try to write some beginner friendly guides in the future when I feel more confident about this ecosystem

Jose Ferreira18:04:27

I'm not saying that people are actively preventing that, i was just making the point that it needs to be done so that the language can get to a bigger audience

Jose Ferreira18:04:30

I'm also down to start some guides when i get that AHah moment with clojure... if you want we can cooperate on that end in a few months...:D

🙂 4
Patrick Truong18:04:54

@jose.ferreira.ptm2 sorry, definitely didn’t want to insinuate you were saying that people were being antagonistic to beginners. I 100% agree that if the goal was to reach mainstream there should be more work for beginners. Likewise, I’d say that it’s a good goal to be more approachable to beginners/anyone even if the goal wasn’t to reach everyone

Mario C.17:04:42

Usually what helps me if I can't find a resource on how to do something in Clojure is I'll just google the problem and omit the word Clojure. See how it is done in JS/Java or whatever and then translate that into Clojure


that mostly works, but avoid things like Spring :D


Look on the bright side: at least has introductory material these days -- when I started to learn Clojure (2010) there was almost no beginner-level material and only a couple of books (Clojure in Action, Clojure Programming, Joy of Clojure were the first three I bought, I think -- 2011, 2012, 2014). Clojure 1.3 was in development. It was a very small ecosystem and community 👀


Important things for me as a beginner… 1. use the repl, write your code and eval, if everthing works, continue adding features 2. read the code of the functions you are using 3. look for examples in github 4. Use the repl again 😁

Patrick Truong18:04:31

Hello Clojurians, I have a quick question regarding Reagent/Hiccup syntax. I was working through a basic Reagent example ( and noticed in the app.cljs file the author used this notation to render a vector of classes without string notation: [:div {:class '[max-w-3x mx-auto pt-12]}. I messed around with it and found that it this notation (`'[max-w-3x mx-auto]` ) is equivalent to just writing the string: max-w-3x mx-auto . Is this standard Clojure syntactic sugar or some Hiccup magic? I can’t seem to find any documentation on this use of the single quote as the docs ( or appear to be talking about delayed eval. Thanks for the help everyone 🙂


@U0122UWNUMC The quote ' means "don't evaluate this". Without the quote [max-w-3x mx-auto pt-12] would be treated as a vector with three symbols that need to be looked up and their values substituted in. In this case Hiccup wants just literal symbols which it converts into strings so you're right that you could use :class "max-w-3x mx-auto pt-12" here (providing the string directly to Hiccup) or you could do :class ['max-w-3x 'mx-auto 'pt-12] -- quoting each symbol to prevent them from getting looked up and evaluated. '[max-w-3x mx-auto pt-12] is essentially a shorthand for a vector where each element is quoted.

Patrick Truong18:04:31

@seancorfield thanks for the detailed response! Is there any style preference as far you are aware over using the shorthand '[ class-1 class-2] vs [ "class-1" "class-2" ] (and I know you wrote some other variations above as well)? Thanks 🙂


I think in the case of Hiccup, using '[class-1 class-2] is preferred since it is less typing and you'll quickly get used to quoted literal data as opposed to working with individual elements/strings.

👍 4
Ben Sless18:04:52

I think another thing which makes Clojure and lisps in general less palatable to the average beginner, is that the people picking them up aren't complete programming novices, but have had prior experience with ALGOL languages. It seems they're suffering the side effects for years, and it tends to make them dismissive of the "esoteric" languages. > Well if LISP is so good and serious why aren't Serious Organisations like NASA using it, hmmm? While in effect they did, until some pointy-haired manager threw it out in favor of C. If Scheme was taught as a first language, maybe things would have been different, but it's very hard to break out of the imperative and OO molds once you've grown into them, it seems. I had new colleagues asking me about design patterns in Clojure, what can you say to that besides "try a function?"

Jose Ferreira18:04:42

Scheme is taught as a first language in many uni

Jose Ferreira18:04:48

it was my first language

Jose Ferreira18:04:20

but is mostly presented as something that you cannot work with, something that is purely academic

Jose Ferreira18:04:00

So it tends to be dismissed by most students, from my experience

Jose Ferreira18:04:38

Well thanks a lot for your input guys, i have to leave...


I don't know if you have tried this, but if learning from a book is a style that can work for you, there are books on Clojure that for not a huge amount of $, can provide a lot of organized material that most people aren't willing to give away for free in blog articles.


Similarly for Eric Normand's on-line videos which cost $. I am not saying that there are no useful free things on-line (there are), but if you restrict yourself to those, you may be missing out on some of the better learning aids.


I think one issue in learning Clojure is in the expectations not being clear. More and more people come thinking, I like to learn by doing a project, maybe put together a little web app. Okay, where's the framework I should use?

Patrick Truong18:04:59

@U0K064KQV as a beginner digging into ClojureScript in particular. I have found that there are opinionated guides on Reagent + Re-frame vs etc. but there just aren’t that many articles compared to looking up “How to build a React project”. There was a comment earlier on how JS has too many guides and with varying quality. I don’t think this is a mark against that community, because as a beginner you can cross reference a lot of guides and it gives you some bearings on what is the accepted best practice/libraries etc. So basically I think there could just be a lot more writings and guides on the ClojureScript (not sure if the situation is different for Clojure for other applications)


I do think it be nice to have more guides or documentation around some of the more common frameworks like re-frame


I think it's different on Clojure. ClojureScript by virtue of being front end specific, there's a lot of reliance on tooling that could benefit from better docs

👍 4

And they hear crickets. So maybe they pick up Luminus. Now they ask, okay, where's the documentation? Where are the guides for using Luminus to build a web app?


And they hear crickets 😋


People say... Sorry, we don't have frameworks. You need to learn the language, and then learn to put together your own custom built framework of choice.


some videos on youtube are helpful for learning how to get a headstart, maybe we could make some videos to onboard newbies fast


this am a repl, this am a lein, etc.


Ya, I've been thinking of trying to start doing video tutorials. Because my attempt at writing a learning Clojure series of blog posts has stalled. There's just too much to teach, writing it is a huge effort, you easily need a book full


But, to go back to my point before. I think there's an expectation mismatch as well. At least for some "newbies". Like they come thinking, hey I heard Clojure is super productive, I heard you can build things things in it quickly, I heard it beats the average. Okay, I'm trying to build a small app, and it's not quick, I am not productive, and nothing shows me how to quickly do it.


If you look at Rails or Django, there's a CLI, you run a command, it scaffolds a whole app, creates your DB tables, schema, creates a homepage, setups admin panels, and all that. One command


In Clojure it's like... Okay, do you know the HTTP spec? 😝


No? Okay, go learn it, you'll need too in order to setup Ring with the proper middlewares.


My point is, Clojure approaches things from "first principles". It does so in a way that is immensely productive. And once you know things that way, sky is the limit, this is why you could then "beat the average". And this is why there is no framework everyone uses, because each person is empowered in a way they can build each app from scratch, and put together the exact framework required for the use case.


But this approach to app building is not one that lets you get started quickly, or put things together from tutorials where you copy/paste bits and pieces. It demands much more initial investment before you see results.


also there's frequently a conflict between matching the domain and matching the universal patterns of the language, and clojure often leans toward the latter

👍 4

which is frustrating when you are new to clojure and know the domain well


but it's awesome when you've learned clojure and now need to approach new domains


I agree. I think there is a huge benefit in knowing and being able to build a minimal web app with just Ring and looking at the request/response abstraction there -- "Just Data" -- and seeing how you layer everything on top with "Just Functions" (e.g., middleware, handlers). It's immensely powerful to see how simple things can be -- and how all these huge, complex frameworks are really not needed for a lot of basic stuff: they obscure "how things work". Clojure really values simplicity over ease of use.

👍 16

Yup, what I don't know is.. what to do to motivate people to stick around until that "light bulb" moment. I think web app might just be the wrong thing to start with for learning Clojure. I think starting to play around with Quil, or some command line apps might be better. Cause you'll get that reward of accomplishment sooner.

😮 4

I really wish Clojure had a Django/Rails/Phoenix framework, but it seems like the community has too many smart people with too many opinions 🙂


it’s so much easier to spend the time and effort getting deep, slowly digging towards first principles, when you have a working app in front of you to hack on


Those are also all frameworks primarily concerned with displaying web pages to people. My experience with clojure is it tends to get used for middleware, data processing, apis.


A former coworker liked to call rails on the front and clojure in the back "the mullet"

😛 12

Hey guys beginner here. I would like to ask what is the best way to have a function have two different behaviors depending on the value of an argument. for example, how do i write the following applying the DRY principle:

(defn foo [x type] 
  (if (= type :a) 
    (-> (type-a-calculation1 x)

    (-> (type-b-calculation1 x)
I'm thinking i could use a bond with-stub but im not sure if thats how it should be.


defmulti and defmethod seem like a perfect fit for this use case


1. DRY is meh 2. That is DRY


You haven't repeated yourself, those are distinct and different things, just with a similar structure


thanks @hiredman im still struggling to know when stuff is too DRY or too WET. I guess its fine in this case.


What you are responding to as repetition is the repetitive structure of the code, but the content isn't repetitive


All limericks are not repititions even though they have the same structure

🙈 4
🎓 4
Michael J Dorian19:04:07

It might be possible to abstract our the structure itself in some cases, but I think that would only make sense for a large volume of uses and a more distinctive structure

Jose Ferreira19:04:24

@chaow Isn't the best aproach here to use polymorphism?

Jose Ferreira20:04:49

whats the difference between quote and var quote?


they are entirely different


var quote is really just part of the syntax for writing a literal var


quote is part of a language feature, quotation which involves pass literal data through the compiler / evaluator without change

Jose Ferreira20:04:55

so i should use quote when i dont want the symb to be evaluated

Jose Ferreira20:04:36

(wrap-reload #'welcome) why does wrap reload need a var quote instead of just quote?

Jose Ferreira20:04:46

shouldn't welcome simply be a symbol?


You want a Var here so you can eval updates to the welcome function and they'll take effect without needing to restart your app.


Without the var quote, welcome will be evaluated (to a specific function implementation) when the wrap-reload call is evaluated and so you would be bound to whatever implementation that function has at the time.


Using a Var adds a layer of indirection, so that when the middleware calls the function at runtime, it'll go through the Var to whatever the current implementation of that function is.

Jose Ferreira20:04:35

this has something to do with state?


This allows you to develop against a REPL running the application, without needing to stop and restart the application.

Jose Ferreira20:04:46

or am i mixing stuff here?


"state" in the sense of what the Var "points to", yes.


Can the same redirection be done through a macro which internally handles the indirection?


which does the same thing #'welcome does


but that's less flexible - what if your handler should be a ref? some other thing which can act as a function but with the reloading semantics (or lack thereof) that you prefer?


is it possible to do partial for functions taking dicts as arg? if i have:

(defn foo [{x :x y :y} :as xy]
     (+ x y))

and I wanted to do a partial that specified the :x argument


that's not possible because x isn't an argument, it's a programmatic extraction inside the function - but you can simulate it with merge


(defn foo-x [x] #(foo merge {:x x} %)) for example


there's no partial syntax for it though


I missed the convo but just in case someone is looking for a great little beginner friendly tutorial on building a webapp with the oft recommended ring/compojure/hiccup route I've found this an excellent guide so far:


@jr0cket's whole site and material out there seem to be top notch and beginner friendly

👍 4
Akshay C. Gollapalli22:04:35

is there a way to check if a function is a transducer (aside from trying to transduce with it?


There is certainly no transducer? predicate function in Clojure. I suppose transducers could have been implemented in such a way to make implementing such a thing possible, but that was not how they were implemented.


I have the following function:

(defn exit-with-status [status]
  (System/exit status))
I want to test the code that calls this function to ensure that it exits abnormally when certain conditions are met. How can I write a test that will pass only when the argument to my exit-with-status function is 1, but without causing System/exit to actually get invoked (which will not be good for my test suite)?


you could potentially start a new process and check to make sure it actually exits with the correct status code


use with-redefs to redefine the exit-with-status function to be a function that just puts the value in an atom, then check the atom


ie, use with-redefs in the test


oh I haven't read about atoms yet, now I have an excuse


personally I prefer to throw an exception rather than call System/exit most of the time


that's fair - in this case I'm writing something specifically to be used on the command line so I figured the error codes were the path of least resistance as it were


Note that System/exit will kill your REPL


right, that is why I need my tests to not call it


if your main thread throws an exception then you'll get an error code at the cli. but you're right that you can't give specific error codes I think


bash-3.2$ clj -e '(throw (Exception. "foo"))' || echo FAIL
Execution error at user/eval1 (REPL:1).

Full report at:



bash-3.2$ clj -e '(println "foo")' || echo FAIL


you could throw in exception in the general case and just catch the exception in your -main for your cli program to produce the appropriate error code


which may make testing/developing easier


you could even throw ex-infos with a general :cli-error-code for the top level catch to return


it’s nice to keep things like System/exit near the “outside” of your program


right; it's only invoked from my main method where any relevant errors are resulting in termination


I'm using clojure.test as well.


Can I, in the context of my test, rebind the exit-with-status function to some function that passes my test if its argument is 1?


Yes, mark it with ^:dynamic and use binding

(defn ^:dynamic exit-with-status ...)
(defn exit-with-status-FAKE ...)
(binding [exit-with-status exit-with-status-FAKE] ... )


there's also this trick: (let [result (promise) exit-with-status-fake (fn [x] (deliver result x))] ...) - you can use the state of the promise ot see if a value was delivered, and what value was delivered


without using an atom which is a more general and powerful feature