beginners

VardriPoise 2025-08-28T13:28:16.518549Z

spec.alpha's rationale page states that it is an objective to separate the specification of the keys of a map from the specifications of valid values for each key. I didn't quite understand where this could be a concern from the general description that was given, it seems to me that it makes more sense to combine the specification of a map's keys along with the valid values for these keys, but then again maybe this would be an objective if spec was instead trying to be a type system but it explicitly states it is not trying to be (and also I'm new to this way of verifying invariants since I come from a static types background, so it's likely I'm missing something/have the wrong perspective or mindset when it comes to verifying invariants in a dynamic types setting). Could someone give me some examples where it makes more sense to distinguish the specification of a map's keys from the specification of valid values for each key, instead of bundling them together? (or maybe a bit more explanation behind the rationale, instead of doing something like e.g. typescript's way of declaring object types which does this sort of combining key+value spec?)

Alex Miller (Clojure team) 2025-08-28T15:42:20.898429Z

one of the key ideas in spec is that fields/attributes are the primary source of specification, not aggregates of them. giving kind of the opposite of what you asked, it is extremely common for different parts of your business to need different sets of attributes of your customer, or order, or whatever - shipping needs the address, billing needs payment details, marketing needs contact, etc. the approaches for handling this in OO/statically typed systems are to either just include everything in some "god" level object/type or to make different (sometimes overlapping) slices of the world and then transform or composite between them.

Alex Miller (Clojure team) 2025-08-28T15:43:24.469979Z

same thing is frequently seen in ETL type transformation pipelines - as you move through stages of processing certain fields are needed for a while and added to the composite, then fall away as you near the end.

Alex Miller (Clojure team) 2025-08-28T15:45:31.532909Z

with Clojure's open map types, you don't need to pre-define the set of fields that make the aggregate, you can just include the ones that make sense at the point in your application where you are using it. each key/field/attribute has well-defined semantics by itself.

Alex Miller (Clojure team) 2025-08-28T15:46:54.903179Z

so your invariants are really primarily about what specification the values of a particular key should conform to, or about the required keys at a particular point, not about defining a rigid fixed "type" representing a domain entity

☝️ 1
Alex Miller (Clojure team) 2025-08-28T16:02:35.026019Z

if done pervasively, this allows new features to be primarily additive - define new fields and their specs, inject them at the point they are needed to the maps flowing through the application, if needed modify code at the point where the feature is being added to use the new field or state it's requirement at that point.

VardriPoise 2025-08-28T22:38:45.250579Z

I see, thank you very much for the extensive explanation!

VardriPoise 2025-08-28T22:39:04.065119Z

I'm starting to make extensive use of spec so I'll be keeping all of these points in mind, thank you!

Melody 2025-08-28T13:42:41.533769Z

I was watching this video this morning: https://youtu.be/R0dP-QR5wQo?si=oYU_nxwVRgpmqOy5 and at around 34:50ish the speaker demonstrates “code focus” mode for rust. Is there such a tool for clojure for vscode or emacs? I think it would be a helpful feature in an editor.

Melody 2025-08-28T13:48:50.581969Z

I already enjoy calva a lot and mostly use vscode and so I already get syntax highlighting over all repeated instances of symbols and the paren matching highlights which are great, but it would be really interesting to see syntax highlighting that reveals related code clearly in the way the presenter shows.

seancorfield 2025-08-28T14:05:31.384519Z

I can see this being very useful in long functions, especially when mutability is involved -- but that's not typical in Clojure code (or shouldn't be!) so I wonder how much value it would really add? That said, I suspect clj-kondo/lsp has enough information in its analysis to be able to build this...?

p-himik 2025-08-28T14:32:21.726759Z

Well, at the very least it could add value when working with projects that aren't typical Clojure code. :) Or maybe further than that. It's easy to feel that something's not that useful when you're accustomed to a different process. I used to never need even the plain symbol highlighting, before I got to use IntelliJ IDEA. Then I made the highlighting of such symbols so prominent that it made the brightest areas in the editor because it turned out to be indispensable. But it was for the first few months, I dialed back the brightness since then. :D It's probably also up to personal preferences. E.g. I definitely don't want the current form to be highlighted - I've tried it and to me it's just noise.

oλv 2025-08-28T15:39:18.386679Z

Not quite the same feature, but Emacs has a feature to highlight arbitrary regexes. I use it all the time :^)

oλv 2025-08-28T15:39:35.648639Z

M-x highlight-phrase or M-s h p

p-himik 2025-08-28T15:59:05.729279Z

Yeah, that seems to be the same thing that I mentioned and that Melody calls "repeated instances of symbols", albeit in at least my case it also tracks the actual value flow - so it's not a random option string but "`option`-the-binding" that gets highlighted. If the name is shadowed, the original name and the shadow are treated as independent and are highlighted separately.

respatialized 2025-08-28T17:33:09.956919Z

I wrote a Clojure-to-Hiccup conversion tool called Adorn that allows for some of this by preserving more of the semantic information that most syntax highlighters don't focus on. In the intro page, I use HTML data attributes to highlight every instance of the core function: site.fabricate.adorn/clj->hiccup, as well as show how CSS selectors can be used to highlight instances of def, defmulti, defmethod, and so on. https://adorn.fabricate.site/#using-adorn-to-highlight-elements While this library is Hiccup/HTML-specific, I wonder about whether a similar approach could be used to annotate tree-sitter nodes with semantic information about the Clojure symbol and data type to help any editors that use tree-sitter implement some of these capabilities.

Ryan A 2025-08-28T17:39:59.241829Z

Two questions: 1. Clojure app organization question and 2. Thoughts on this as a way to learn Clojure or any other language. I created a Clojure app as a scratch pad for practicing what I learn in Clojure from various sources. In one namespace I have a file that I added a bunch of questions/exercises to. Each wrapped in its own comment. I'm up to over 50 questions. Answering them top down doesn't make a lot of sense so I moved each question into it's own map. The collection of maps is inside of a vector. For example, here's a question I pulled directly from the Clojure Syntax guide { :exercise "Using the REPL, compute the sum of 7654 and 1234." } . I then wrote a function to randomly pull a question from the collection.

(defn random-question
  [exercises]
      (nth exercises
           (rand-int
            (count exercises))))
Given the number of possible questions (billions and billions!) this vector could grow quite large. A database seems like an obvious choice to store the questions, but that's too much cognitive overhead for me at the moment. Is storing that vector in it's own file in the same namespace as the random-question function the "right" way to do this? Or is there a better place in the project to store data like this? The app was created using Leiningen so it has the basic file and dir structure that creates. I'm mostly interacting with this via the REPL but I could see compiling it or turning it into a web app, but that's above my current ability level. I like storing the exercises in the map. I envision adding keywords for tagging the questions { :tags [ "syntax" "clojure.core"] } and maybe other keywords for "learned" or even Anki style metadata like { :anki/sm2 { :due-date a/due/date :last-reviewed some/other/date :interval 3 } } Sorry, this is longer than I intended. My questions: 1. Where should I store the data in my app? and 2. Thoughts on this approach to learning Clojure?

2025-08-28T18:22:49.449179Z

as a tiny thing: (def random-question rand-nth)

2025-08-28T18:24:11.168599Z

"in it's own file in the same namespace" - this statement does not align with my understanding of clojure namespaces. I am used to namespaces mapping 1:1 with specific files

Ryan A 2025-08-28T18:26:18.262709Z

Great, thank you @noisesmith. That's documented right below (rand-int) . That could be. I'll reread the docs on namespaces. Thanks.

2025-08-28T18:26:19.670639Z

more generally, namespaces should map deterministically to a single entry on classpath (under development that will be a file under the project root, as a dep in another project it will be a resource in a jar)

2025-08-28T18:27:27.830619Z

the reason for this is that when you use require only the first match is used - you can have a resource or a secondary namespace, but you can't implement one namespace in two places, only the first one is used

2025-08-28T18:30:00.511759Z

you could have a "twin" .edn file - eg. if code is in ns foo.bar , you could load (io/resource "foo/bar.edn") in that ns as a way of keeping it clear that that data belongs with that ns

Ryan A 2025-08-28T18:30:09.958179Z

You are correct, from the docs "Due to this loading convention, most Clojure is structured with a 1-to-1 mapping of namespaces to files, stored in hierarchical fashion that maps to the namespace structure."

2025-08-28T18:30:52.200209Z

io/resource will transparently load a file from classpath for local usage, or a resource from the same relative location in a jar on classpath

Ryan A 2025-08-28T18:31:32.780549Z

When you say "transparently load a file from classpath for local usage" do you mean I won't have to require it?

2025-08-28T18:32:20.629559Z

I mean that the same io/resource call will find the input data, whether it is in a file or in a jar

2025-08-28T18:32:49.003209Z

you would need to translate that input source into parsed data manually though, and require would have nothing to do with it

Ryan A 2025-08-28T18:33:42.802019Z

Awesome, thank you! This project is a little ahead of what I know currently. I appreciate the help and feedback.

2025-08-28T18:33:58.879639Z

this could be an advantage - eg. if you wanted to load up a sqlite db instead of an edn file, io/resource could do that too, it is just an input source

2025-08-28T18:34:48.449339Z

(since you mention potentially using a db, sqlite is a straightforward choice for bundling into a clojure app)

Ryan A 2025-08-28T18:37:03.094379Z

For sure. I'm a ways out on that, but I'd like to make the best decisions I can early on.

2025-08-28T18:37:13.156199Z

I think of require as having three parts: • an abstract location that works transparently whether pointing at a file or a part of a larger blob of data like a jar • parsing and loading code • book-keeping to prevent things from being loaded twice unless explicitly asked to we only need the first part here, and that is what http://clojure.java.io/resource does

2025-08-28T18:38:09.936189Z

another popular option for data bundling is a portable data format like csv or json

Ryan A 2025-08-28T18:39:18.672419Z

Good point. I hadn't considered either of those. Both of which I'll need to learn how work with eventually.

gaverhae 2025-09-01T14:07:35.557849Z

Last I checked (and that was some time ago, so it's possible this is no longer the case), the JVM did not have a good understanding of "data", which meant that it could actually be problematic to have a large data structure in Clojure source code files - because the compiler has to generate the Clojure code to recreate that structure, instead of being able to save the data structure itself, and that can generate a bigger chunk of code than the JVM can handle. Loading an EDN file at runtime does not have that issue, though. Which, like noisesmith suggests, points to using an EDN file. The traditional way to do this would be to have the EDN file under resources instead of src, because it src is for source code and data is not source code (arguably). A combination of and clojure.edn should get you there. When you get to compiling your application into a jar (fairly easy with Leiningen), the resources folder will automatically be included in it, in such a way that can find it.

Ryan A 2025-09-01T15:01:03.159999Z

Good to know, thank you. I ended up using and EDN file per noisesmith's suggestion. However, I wanted to pull it into multiple namespaces so I placed it in resources after doing a little more digging. I also pared down my files to a single file per namespace per noisesmith and the documentation's recommendation. The EDN file is pretty small, 96 maps inside of a vector. Each map has at most 6 keys. Once I get more comfortable working with that data I'll probably put it into a database. Building an Anki clone sounds like a fun project and I've already learned a bunch working on it. Right now it's basically my Clojure playground.

ChillPillzKillzBillz 2025-08-28T18:27:33.399399Z

Hello, I have this strange problem trying to create a websocket from the java-http-clj example page

(ns livedataproc.WebsocketServe
  (:require [java-http-clj.websocket :as ws]
            [java-http-clj.core :as http]))
(def localsock
  (ws/build-websocket
   ""
   {:on-text (fn [ws string last?]
               (println "Recieved some text!" string))
    :on-binary (fn [ws byte-array last?]
                 (println "Got some bytes!" (vec byte-array)))
    :on-error (fn [ws throawable]
                  (println "Uh oh!" (.getMessage throawable)))})
On running this piece of code I get the following error
; Execution error (ClosedChannelException) at sun.nio.ch.SocketChannelImpl/ensureOpen (SocketChannelImpl.java:195).
; null
I'll greatly appreciate any help/pointers as to what I am doing wrong. Many thanks

gaverhae 2025-09-01T14:13:11.846459Z

The error messages means you're failing to connect. The code you provided is trying to create a client connection to what it expects to be a WebSocket server running on localhost:8080/ws; the error message suggests that port 8080 is not open on localhost. I.e. you don't have a websocket server running at the expected address.

ChillPillzKillzBillz 2025-09-02T06:49:55.821119Z

ah ok. I got it now. The documentation is a bit confusing for me. I thought I was created a websocket server. I'll dig deeper. Many thanks!

2025-08-29T14:11:16.095519Z

hi! Do you have a websocket server running on 8080? If I run it against an online test websocket it seams to work :

clj -Sdeps '{:deps {java-http-clj/java-http-clj {:mvn/version "0.4.3"}}}'              
Clojure 1.12.0

(ns livedataproc.WebsocketServe
  (:require [java-http-clj.websocket :as ws]
            [java-http-clj.core :as http]))

(def localsock
  (ws/build-websocket
   ""
   {:on-text (fn [ws string last?]
               (println "Recieved some text!" string))
    :on-binary (fn [ws byte-array last?]
                 (println "Got some bytes!" (vec byte-array)))
    :on-error (fn [ws throawable]
                  (println "Uh oh!" (.getMessage throawable)))}))
Recieved some text! Request served by d56832234ce08e
#'livedataproc.WebsocketServe/localsock