Fork me on GitHub
Danilo Oliveira05:02:57

My first reagent code. I'm also a Clojure learner. I just translated a react sample application to reagent. Anything I could improve?


I would add ^:dev/after-load to mount-root rather than having a separate reload! function. In production builds, the metadata will not do anything, so it is safe to mark mount-root with it


One more note: reagent supports React 18 now. The new reagent.dom.client namespace should offer you the newer rendering functions introduced in React 18

Danilo Oliveira08:02:31

Those lines of code came from leiningen reagent template, thanks for letting me know that!


Hey, I noticed that you are closing over multiple reagent atoms in your my-feature function (factory?). I haven't used reagent in years, but in your case you might prefer to write it differently: • a single atom that holds all the UI state in a map, defined with defonce • top level (`defn` ) functions that operate on that atom. This allows you to use/test these in isolation. And reference them a bit more easily in your UI components. Look if something like that is feasible for your example and maybe compare if you like it more or if there are trade offs that are useful to think about.

Danilo Oliveira12:02:44

I translated mechanically the same react code, and I translated every state hook into a single atom. What you wrote makes sense actually, thank you!

🚀 2
Ivan Quirino16:02:56

isn't the drawback of using a single atom that it will re-render everything on the app? Isn't that why redux and re-frame exists?


React diffs the vdom before it renders something. I think re-frame and redux (with subscriptions, included in RTK) prevent React from kicking that off in the first place, because it compares the state values, including computed and derived values. So it's basically another layer before that. But for the general case, you don't need to worry about any of this anyway. And even "naively" written React doesn't get noticeably slow in my experience, even for complex forms with deep validation rules or animated, interactive drag and drop interfaces etc. Re-frame and redux are really more about code organisation rather than performance from what I know. I would assume that after you add enough data to your atom and have tons of computed and derived values, then you could notice performance gains from using subscriptions that update specific rendering atoms and so on.

🔥 2
Danilo Oliveira07:02:43

thanks for the elaborate answer, @U01EFUL1A8M

👍 2
Arya S07:02:04

How do I set a data attribute on a DOM element in clojurescript? I can write (set! (.-innerHTML newElement) (.-val note)) and see that it updates the markup, but (set! (.-data-frequency newElement) (.-val note)) does not set data-frequency attr

Arya S08:02:33

Found it, it's .setAttribute instead (.setAttribute newElement "attrname" value)


Glad you found it. If it helps on other stuff, I do a lot of direct DOM (and SVG!) manipulation in Web/MX:

C Ross13:02:09

I am really interested in the ClojureStream learn Re-Frame course but it seems to use depreciated tools. Should I discount the course because of this?


Which tools do you think are deprecated?


Having done some of the ClojureStream courses I found they very useful, even if I used different tooling. Tools and libraries may evolve but core Clojure code and design is very stable and works regardless of tooling choice. Particular tool choice in learning materials should not detract from the information that can be learnt, otherwise all learning material would quickly become irrelevant For example, I recently migrated to Clojure CLI tools although the code is the same if Leiningen was used instead (just a different config file) I also been using different this year and the vast majority of things are the same, even between Emacs and Neovim


as far as I can get from FAQ section - "The focus is on ClojureScript and Re-Frame. We'll not touch any CSS; even thought we'll use styled components, nor create any endpoints, everything is taken care of." So even if it depends on depreciated library the main point of learning is to get you up on ClojureScript and Re-Frame. I think if you can learn how to use javascript dependencies in your app you can use any of them.


Yes, I agree with you on that.


The best entrypoint is to go and check to re-frame documentation


Agreed that finding a relative starting point is a challenge as there are many options and opinions across every programming language. Re-frame can get quite advanced. I'd suggest starting with reagent and figwheel to get a foundation Also take a look at I prefer using Figwheel to develop ClojureScript projects, using this template to start new projects There is also Shadow-cljs if a node.js centric approach preferred This is a nice ClojureScript reference Then start looking at Re-reame if building more complex web user interfaces. has some content, mainly around figwheel and single page apps. Not the most detailed book in the collection though


Yes. Every Lisp has traditionally been hostile to newcomers. Some of us playfully suggest we are deliberately discouraging The Unworthy, but it is just a shortcoming. We need a gigantic corporation to take us under its wing and finance polish. That is not going to happen. So the barrier to Lisp entry will always be high. Including library/tool bit rot. And do not get me started on Clojure tooling. :rolling_on_the_floor_laughing:


there are lots of great clojure beginner resources


many of them free


@kennytilton shared their opinion about things, they are just some random on the internet (as am I), so who knows


getting started with clojurescript, in my random internet opinion, is terrible because you can't escape the tired fire of js dev


the paid course you found is also another random person on the internet who at one time made a course and put it up with a price tag on it


the correct forum for complaining about that course is whereever the course is, not here, because in all likelihood no one here had anything to do with it

Alex Miller (Clojure team)18:02:51

let's maybe redirect to finding a resource that is good for Christopher

Alex Miller (Clojure team)18:02:22

are you looking more for language stuff, or stack stuff towards accomplishing that, or tooling?


react, and all its wrappers and work alikes, are sophisticated engineering products, tools to solve business problems. they internally may apply bits of computer science(maybe in the form of diffing algorithms or sorting, or whatever), but it is almost their purpose not to leak that stuff to users


so if you are pursuing the use of those engineering products, it will take a lot of work with them before enough interesting CS details leak out for you to pick interesting stuff up by osmosis


so again, if you are interested in using clojure and clojurescript to explore concepts in computer science, starting with something like react (or re-frame or reagent, or whatever) and the complicated tooling required there to get a good dev experience (shadow whatever) is not going to be the best path


there are a few great intro to cs courses based around lisp, structure and interpretation of computer programs being the traditional one, the text of the book is free online and their are several lecture series on youtube that cover that material. the mit sicp lectures from 1986 are on youtube and they are great. the other one I have less experience with, but have heard good things about is how to design programs.


both those computer science resources use dialects of scheme, but the principles you learn are transferable to any language


so those are some computer science resources if you want to pursue that, if you want to pursue react/re-frame/reagent and clojurescript there are resources for that linked above

Alex Miller (Clojure team)19:02:03

going back to your original question, I think you'd learn a lot taking the course, presumably enough to evaluate other tools to decide what is best for you

Alex Miller (Clojure team)19:02:31

if your concern is about Leiningen, I would not consider that deprecated or an issue (it's unclear to me what you were worried about)


@U02SPT52J5S If you ar making an SPA in Clojurescript in 2023, just do I suggest this esp. because you say you are not a front-end person. re-frame is a better version of conventional JS frameworks, but Web/MX breaks the mold when it comes to eliminating the hoops thru which F/E devs must jump. And I will be delighted to mentor you. Speaking of which, just grab a library and start with their tutorials and come back here with questions. Even better mentors than I will jump in.


As someone who has always been a backend guy, I sympathize about the apparent complexity -- and the huge degree of churn in libraries and tooling -- around modern frontend development. About a decade ago, I had a go at frontend work in ClojureScript with Om, then with Reagent (which I liked a lot better), but decided the tooling was all a bit fragile and the differences from Clojure were annoying so I set it all aside... I had another go at it last year (or maybe 2021?) with re-frame and got further with it, but it still seemed way more complex than building backend stuff (server-side rendering HTML pages) so I didn't pursue it much. HTMX seems like a really good compromise and I'll probably have a go at building an app with that at some point. Maybe also try Biff (based on HTMX but also assumes a lot of other stuff in the stack, such as XTDB last I looked). Is Web/MX similar @U0PUGPSFR? (and I've been happily doing backend work in Clojure for over a decade now, in production, but I still find the frontend stuff all a bit alien and bewildering)


@U04V70XH6 wrote: "...I still find the frontend stuff all a bit alien and bewildering" You are in good company. Mr Hickey swore off F/E in his anniversary talk. The logic to implementation ratio was too small. "I do not work on that stuff." Meanwhile, HTMX has me concerned. The idea "Just do HTML!" is indeed seductive, but then I look at the attribute spaghetti as examples scale and...well, that is my concern. As Joe Louis said, "You can run from the complexity of U/X programming, but you cannot hide." I think he said that. I just looked at Biff, had not heard of it. Does it have an execution of the TodoMVC standard demo? It is not a great standard, but I learn a lot from seeing its implementation in any given Blub. Links welcome. I think the closest thing to Web/MX might be the original MobX, before it got sucked into the mainstream turbines by (a) supporting React and (b) getting dumbed down to MobX State Tree, to make its protein more recognizable to Flux...afficionados. Web/MX, unlike anything else I have seen, and I love hearing about new similar hacks, is: • all reactive all the time (not just the UI, also XHR, localstorage, timers, endpoints...); • achieves reactivity transparently -- no publish or subscribe; • property-to-property dependency -- no "view functions", level 1, level whatever; • leaves HTML and CSS (or React or RN or Flutter) alone by wrapping them thinly -- their doc is our doc. Maybe I can convey all that in the Web/MX announcement RSN. I plan to offer at the same time sth evolving an app, the way I did here with the JS version: Free onboarding help to anyone.


I should ask this here too. 🙂 Anyone else experienced high CPU usage with such 'JAR-less' deployments?

Alex Miller (Clojure team)14:02:02

there's nothing in Clojure itself that is using CPU unless you ask it to. so either it's something in your app, or the JVM GC is shredding itself. there are lots of tools to analyze which of these it is and diagnose. I would start with a simple thread dump (jstack or even just kill -3 pid)

👍 2

What do you mean jar-less?


Is it using tools.deps to pull down dependencies?


Ah, ok, well not sure what these 3xx and 4xx are about.

Guild Navigator17:02:26

I have a map like this :

(def test-map {"key1" "val1" "key2" "val2" ... "keyN" "valN"})
where N can be large (e.g. 200). Any way to format this automatically so each k/v pair is on its own line?


do you want your code to be formatted, or just some pretty print output?

Ben Lieberman17:02:20


Guild Navigator17:02:45

I want the code to be formatted, not the output.


you can copy and past the pretty print output into your code

Guild Navigator17:02:57

I'm used to a formatter like black in Python or go fmt in Go that would do this kind of thing for me on save.

Ben Lieberman17:02:27

if you're in Calva ctrl+shift+i should format maps like that

Guild Navigator17:02:41

oh nice! thank you! yes i use VS Code with Calva

Guild Navigator17:02:46

let me give that a test


There is also cljfmt if that works for you.

Ben Lieberman17:02:34

well, with the caveat that if it's all on one line it won't, but if you put some newlines in select places ctrl+shift+i will line up the keys


you can also represent a Map as a vector of vector kv pairs, it's possible that your editor formats that better. you can do (->> my-vector-pairs (into {})) to convert into a Map


for large data structures i use a variety of formatting tools to get them to look good, but they are all in emacs. i use a tool that lets me to alignment of things based on regex (eg: align all ":" characters). if you have a tool in your editor that can do something like that, it could help a lot when you have more complicated formatting requirements

Guild Navigator18:02:44

I'll have to see what VS Code can do with just Calva


also consider having your data in a .edn file (there may be better formatting for that, but it's completely editor dependant)

☝️ 2

everything you treat as a code has to pass through the compiler and be serialized to bytecode and then deserialized back to clojure data

Guild Navigator18:02:19

Looks like Calva does a pretty good job out of the box on save:

Guild Navigator18:02:56

that was a verbatim paste from Go, modulo the // comments i had to change to ;;


if you have a large amount of pure data it is better to but it in a file and read it as a resource at runtime that way it doesn't need to go through the compiler and roundtrip through bytecode for no reason

Guild Navigator18:02:02

its a list that doesn't change very often - i'm fine with it compiled in. It's 330 keys and won't double in size anytime soon

C Ross18:02:28

Just wanted to get started with Clojure.

That is not going to happen. So the barrier to Lisp entry will always be high.


if the tool is solving a problem that is very hard to solve with what you are familiar with, the barrier seems a lot lower.


Is there some tool you are using that outputs this message? "That is not going to happen. So the barrier to Lisp entry will always be high." If so, it is not obvious which tool you are using that outputs that message. I'm pretty sure it is not the output of the Clojure compiler, for example.


This sounds like a meta discussion that might be better off in #C03RZGPG3 (esp. if it relates to ChatGPT which I'm beginning to suspect...).


(The message was taken from a discussion in a previous thread in this channel but yeah it confused me too when I read it)


i was confused as well, but made a response as if the second part of the message didn't exist, as it seemed nonsensical. i think it would help if @U02SPT52J5S replied to the original thread instead of making a new one


Ah, Kenny telling beginners that Lisp hates newbies and so Clojure must hate newbies too... 😞


don't all programming languages hate newbies, except for squeak/smalltalk 🙂


I never really considered Clojure a true Lisp until I saw how noob-unfriendly it was. 🙂 Lemme go find a famous quotation on this subject. brb...OK. An argument against an FAQ in favor of forum expert assistance:

Every week we see noobs stumble in the door, trip over the upturned rug, bang their heads on the too-low hanging lamp, and then ease into Lisp pretty easily after being pointed to Lisp-aware editors, Slime, PCL, etc etc directed by living Lisp Gods, dynamic and interactive, not some two-dimensional billboard of an FAQ that just raises more questions. 
Remind anyone of the #C053AK3F9 channel?


That's a pretty specious (and irrelevant) comment given that a) Clojure has an FAQ b) you can open issues about errors & omissions and c) if you've e-signed the Contributor Agreement (for copyright sharing purposes), you can easily send a Pull Request for a change... So, no, it doesn't remind me of the Clojure community and I wish you'd stop popping into #beginners threads and telling folks who are trying to learn Clojure that "because it's a Lisp, it's miserable for beginners". Either answer specific questions beginners ask or don't contribute.


tbh, I think beginners would benefit greatly from knowing their growing pains are normal. A good teacher is someone who remembers what it is like not to know, and thus is able to encourage students by sharing their own growing pains. ie, "No, it is not just you."


Right, I don't see how these "growing pains" are specific to Clojure/lisps though. If you visit any other developer community I think you'll find that beginners there struggle just as much. 🤷


Exactly! It's not "It's hard because Clojure is a Lisp and Lisps hate newbies", it's "It's hard because you're unlearning most of what you know and learning a whole bunch of unfamiliar techniques -- and here are some great resources to help you understand some of this [points at any number of useful articles including Clojure's own getting started material and FAQ and list of books and list of communities where you can get help and maybe there's a local user group on this list of meetups]". The former stance -- yours, Kenny -- is denying the huge amount of work that has gone into improving the beginner experience, on in particular but with Clojure in general, over the last five years or so. It's really counter-productive and I'm asking you publicly to stop doing it here in #beginners If you want to grumble about that sort of stuff, feel free to do it in #community-development or #off-topic Please just stop doing it in this channel, OK?


And, to be crystal clear about my seriousness on this issue: if you persist in this channel, I will remove you from it; if you return and continue doing it, I will remove you from this community. You are undermining a lot of people's effort and you are not helping beginners by trying to draw parallels with classic Lisp's failure to serve beginners.


You are of course welcome to contribute to improving the beginners' on-ramp on instead of just complaining about it...

Guild Navigator19:02:04

clj-kondo is giving me "Unresolved symbol: spend-record":

(defstruct spend-record :customer :service :spend :rank)

(def anycompany-spend (struct spend-record "AnyCompany" "EC2" 123456.78 1))
It compiles fine. Am I doing something wrong.


clj-kondo has no support for defstruct which is an old construct in clojure, mostly replaced by defrecord

Guild Navigator19:02:57

oooo ok let me change that


it should have support for it though, but because almost nobody asked for it, it never was done ;)

Guild Navigator19:02:04

well if defrecord is the current idiom, i'll use that


I think you should mostly just use maps, unless you have specific reasons not to

Guild Navigator19:02:45

i'm still thinking "strongly typed"


no worries, it's fine :)

Guild Navigator19:02:49

but to answer my own question, switching to defrecord solved the clj-kondo issue

👍 2
Guild Navigator19:02:49

"use a regular map" it is!

Guild Navigator19:02:15

i guess i'm stuck in the mindset that structs enforce naming at compile time and guard against typos

Alex Miller (Clojure team)19:02:47

except they don't do that either :)

❤️ 2
Alex Miller (Clojure team)20:02:44

structs and records are open maps too, so you can add other fields to them


And probably worth mentioning that if you dissoc a declared field from a record, it stops being that record and becomes a regular map -- which is something that can catch people out:

user=> (defrecord Example [a b])
user=> (->Example 42 "Hello!")
#user.Example{:a 42, :b "Hello!"}
user=> (type (->Example 42 "Hello!"))
user=> (keys (->Example 42 "Hello!"))
(:a :b)
user=> (dissoc (->Example 42 "Hello!") :b)
{:a 42}
user=> (type (dissoc (->Example 42 "Hello!") :b))
By removing a declared field -- :b -- we turn the user.Example record into a plain hash map (well, an array map since it has so few elements).

Guild Navigator20:02:46

Why do I see Feb 01, 2023 3:24:46 PM$eval472$fn__475 invokein my output? Any way to shut this off? I only want to see INFO ERROR, etc.


Are you seeing other output, or just that one line? What is in your source code that is producing that output?


that looks like the default log formatting for java.util.logging


the default formatting is multiline


there will be a line like that with the data, the class and method where the logging occurs, and then the next line is the log level and message


usually the way people deal with java.util.logging is they redirect it to some other logging framework (like log4j2)


but you can also configure jul via a configuration file or programatically alter how it logs

Guild Navigator00:02:21

yeah i see my INFO ... but I'd like more control over the formatting


My macro-fu is weak. Could someone help me write a macro that, during map destructing in defn, verifies the keywords against a list in the namespace? Signature would be the same as defn. Goal is to prevent typos in keywords. For error signaling, exception or even log is fine.

(def valid-keywords [:f-name :l-name :phone :company])

(defmacro defn-validated [name doc-string? attr-map? [params*] prepost-map? body)] 
This way, you would get an error when you try:
(def john-smith {:f-name "John"
                 :l-name "Smith"
                 :phone ""
                 :company "Functional Industries"
                 :title "Sith Lord of Git"})

(defn-validated print-contact-info [{:keys [f-name l-name phone company title]}] )
…. because :title isnt’ a valid keyword, according to valid-keywords thanks so much!


Here is just enough to get you started. Note that the beauty of homoiconic macros is that we can write any Clojure we like to produce the expansion:

(def valid-keywords [:f-name :l-name :phone :company])

(defmacro defn-validated [name [params*] & body]
  (prn :params!!!!!!! params*)
  (let [keys (map str (:keys params*))]
    (prn :keys! keys)
    (if (every? (fn [key] (some #{(keyword key)} valid-keywords)) keys)
      `(defn ~name [~params*]
      (prn :invalid-keys (remove (fn [key] (some #{(keyword key)} valid-keywords)) keys)))))

(def john-smith {:f-name "John"
                 :l-name "Smith"
                 :phone ""
                 :company "Functional Industries"
                 :title "Sith Lord of Git"})

(defn-validated print-contact-info [{:keys [f-name l-name phone company title]}]
  (prn :BAM f-name l-name phone company title))
When I compile, I see:
(defn-validated print-contact-info [{:keys [f-name l-name phone company title]}]
  (prn :BAM f-name l-name phone company title))
:params!!!!!!! {:keys [f-name l-name phone company title]}
:keys! ("f-name" "l-name" "phone" "company" "title")
:invalid-keys ("title")
If I add :title to the valid keys then (print-contact-info john-smith) will work. Hth.


btw, to achieve the optional parameters I left out of this solution, you will not be able to have destructuring [params*] in that fixed position. The destructuring will have to wait until you know where params* landed.


Use spec?


And skip the macro all together... doesn't answer your question but maybe it's a better approach?


thanks, @U0PUGPSFR! i have so much to learn about macros 😉


@U04JJEM2X1V: spec is always an option, but it’s really overkill for just this scenario