beginners

amiorin 2026-02-25T09:45:08.400799Z

I am exploring Clojure macros to eliminate boilerplate when composing complex, nested workflows. • Workflows: Functions that process a "threaded map" through a series of steps using a loop/recur pattern. • Namespacing: To prevent key collisions, I use qualified keys. However, some workflows share keys (e.g., :shared-ns/name), complicating state management. • State Management: I’ve moved toward using an atom for options to handle nested workflows. If a top-level workflow myns/walter contains sub-workflows myns/ansible and myns/tofu, the options map mirrors this hierarchy: {:myns/ansible-opts {...} :myns/tofu-opts {...}}. • Execution Logic: The engine must distinguish between a "leaf" step (regular function) and a "sub-workflow." It must also handle "option migration"—where specific data (like an IP address from a Terraform output) is mapped into the input of a subsequent sub-workflow (like Ansible). The goal of the macro is to create a DSL that accepts a simple data structure representing these dependencies and automatically generates the wiring, state-shuffling, and namespacing logic. This is the code that I need to generate with the macro: https://github.com/amiorin/walter/blob/f7629fad0ea18003852799e7ce4189dc2b5f9ab3/src/comp_wf.clj#L28-L63 Is there something that I should be aware of before trying to write this macro.

amiorin 2026-02-25T10:07:00.861489Z

I'm trying first with a function.

p-himik 2026-02-25T10:20:22.762979Z

Yeah, the first step after deciding that you probably need a macro is to forget about it for some time and try with functions instead. :)

➕ 3
😃 1
amiorin 2026-02-25T12:46:44.941059Z

The fn is enough. 🎉

🎉 2
seancorfield 2026-02-25T14:39:10.702449Z

The first rule of Macro Club is... 🙂

2026-02-25T14:48:01.487849Z

yeah, start with an fn and see how far you get. generally, the things that make macros nice are when you need a & body param that you don't want to force users to wrap in an anonymous function, or you need information about the environment (line numbers on the called form)

Shantanu 2026-02-25T18:59:59.374799Z

Hi, this is a slightly open ended question. I'm trying to understand what the purpose of :preserve :read-cond mode in read & read-string is. Does anyone have experience where they needed this? Also, once I have the reader conditional, how do I actually evaluate it?

user=> (read-string {:read-cond :preserve} "#?(:default 0 :jank 0 :clj 0)")
#?(:default 0 :jank 0 :clj 0)

user=> (type (read-string {:read-cond :preserve} "#?(:default 0 :jank 0 :clj 0)"))
clojure.lang.ReaderConditional
--- Full disclaimer I'm trying to implement read-string in jank, so please let me know if this type of question is not meant to be asked here.

phronmophobic 2026-02-25T19:05:30.653029Z

> how do I actually evaluate it? What do you mean by evaluate? Usually, the goal of read is to turn a stream of characters into data structures without any evaluation occurring.

phronmophobic 2026-02-25T19:07:32.685019Z

> I'm trying to understand what the purpose of :preserve :read-cond mode in read & read-string is. Sometimes, it's useful to treat code as data (eg. code analysis). In those cases you want to represent the code as data.

Shantanu 2026-02-25T19:10:22.883179Z

I suppose my main question would just be what's the potential usages for this feature. Code analysis gives me some hints. Thanks for that! The most basic usage in my mind is evaluation so I assumed if we can read it in while preserving it, there might be a subsequent evaluation stage.

Shantanu 2026-02-25T19:11:53.723449Z

I made a mistake of prematurely reworking the parser and it bit me, so this time I'm trying to understand the purpose first 😛.

phronmophobic 2026-02-25T19:12:12.962509Z

I don't think it's actually used that often. You can search for uses of :preserve , https://cloogle.phronemophobic.com/name-search.html?q=preserve&tables=keywords

👍 1
phronmophobic 2026-02-25T19:13:28.137799Z

You can also browse usages of :read-cond, https://cloogle.phronemophobic.com/name-search.html?q=read-cond&tables=keywords. Many of the usages are just faithfully reimplementing its usage.

phronmophobic 2026-02-25T19:14:46.799449Z

I think shadow-cljs, tools.reader, analyzers, etc might use it for analysis.

👍 1
phronmophobic 2026-02-25T19:15:50.409459Z

For many analysis use cases, you also want more info about the actual character stream. For those use cases, folks just use something like edamame or tree sitter (which are alternative readers).

dpsutton 2026-02-25T19:16:35.144609Z

util=> (get (read-string {:read-cond :preserve} "#?(:default 0 :jank 0 :clj 0)")
            clojure.lang.ReaderConditional/FORM_KW)
(:default 0 :jank 0 :clj 0)
util=> (read-string {:read-cond :allow} "#?(:default 0 :jank 0 :clj 0)")
0

dpsutton 2026-02-25T19:16:44.142669Z

neat

😎 1
Shantanu 2026-02-25T19:17:43.271349Z

@smith.adriane Thanks for your input, I'll try to check a couple of usages and see if I can gleam other use cases else I have some idea for its usage and subsequent implementation in jank.

phronmophobic 2026-02-25T19:19:38.898469Z

dewey also has a sqlite data dump where it's plausible that you could write a sql query to could find all the lines of code that use read/read-string near other lines that have :read-cond, :preserve.

phronmophobic 2026-02-25T19:20:40.567369Z

Some day, I'll setup a datalog db where you can just run queries.

🔥 1
Alex Miller (Clojure team) 2026-02-25T19:26:26.028459Z

I implemented this originally so happy to shed some light. Conditional reading allows you to read in the context of a particular platform and conditionally choose what is read. This is allowed in cljc files, not allowed in clj files etc. in some cases, tools (like an analyzer) need to read and preserve the conditional read construct itself so that it can be re-emitted, evaluated, analyzed, whatever

Alex Miller (Clojure team) 2026-02-25T19:27:57.817319Z

There are some docs about this in read https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/read

Alex Miller (Clojure team) 2026-02-25T19:30:03.719239Z

Also https://clojure.org/reference/reader#_reader_conditionals

Shantanu 2026-02-26T15:14:57.961979Z

Great, that helps! I think I'll get the implementation right with this. I was wondering why there was a TaggedLiteral runtime object when reader suppression was being turned on. It makes sense now. Thanks!