beginners 2025-11-27

Anyone have recommendations for a good deductive database in Clojure? Was considering XTDB/Datomic/Mangle - the use case is clinical documentation and decision support so it should be bi (ideally tri) temporal, support Datalog and vector search

Perhaps you get answers in the respective channels #xtdb and #datomic

I have a question about variadic functions: what is the "thing" passed into a variadic function? Is this also some sort of clojure data structure or is this just handled by the reader?

You can pass in anything that fulfills ISeq
this part of an above question from @gregorybleiker confuses me a bit - the runtime generates the sequence when you call a variadic function, "you" wouldn't do so

Not sure what you mean? The & notation to indicate a sequence of args?

In Java, a variadic function's final argument is technically an array

Which may be immaterial to OP's question

Yeah, Java interop is a whole other can of worms with variadic functions until that gets addressed (in Clojure 1.13, hopefully).

(defn f [a b c] ...)
(defn g [& args] ...)

(f 1 2 3) ;; f is passed three arguments: a = 1, b = 2, c = 3
(g 1 2 3) ;; g is passed a single argument: args = (1 2 3) -- a sequence

As an example:

(List :a :b :c)
what is :a :b :c in terms of a data structure?

@gregorybleiker Does my code snippet above help?

a sequence is a protocol, correct. @seancorfield?

Sequence is an abstraction in Clojure.

👍 1

protocols are more like interfaces

What's your programming background @gregorybleiker? (so we can try to map Clojure concepts onto stuff you're already familiar with)

I've done a lot of c#, javascript, some haskell and lots of others. The link is very good, thank you @seancorfield

ok, so taking C# variadics as a reference, the main difference is that what constitutes the fixed part of the arglist is determined at invocation time rather than statically

I see there's sequence and seq and the ISeq interface. From my background, the ISeq makes most sense. You can pass in anything that fulfills ISeq . Is that correct?

seq tries to produce a sequence from something. ISeq is an implementation detail you don't need to care about. sequence is a transducer function that produces a lazy sequence.

user> (defn f [& args] [(sequential? args) (type args)])
#'user/f
user> (f 1 2 3)
[true clojure.lang.ArraySeq]
user> (f 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21)
[true clojure.lang.Cons]
The answer to the question, 'What is the literal type of args ?' is 'It depends' ... But that's not important. What is important is that the value of args is sequential?. simple_smile

seq? seqable? and sequential? are available. seq? is not very useful -- it specifically checks whether something implements ISeq under the hood. seqable? checks whether you could call seq on something, i.e., can you turn this into a seq (a sequence). sequential? is true for data types (collections) that have a natural order of traversals (arrays and lists).

Thank you all!

A lot of Clojure's implementation types are exposed from its Java underpinnings but you can mostly ignore them and work with the abstractions in the Clojure language and clojure.core.

Another wrinkle; interoperating with some Java variadic methods requires an array. e.g., java.nio.files.Paths/get(String first, String... more) Needs:

user> (java.nio.file.Paths/get "/home" "harold" "Downloads")
Syntax error (IllegalArgumentException) compiling java.nio.file.Paths/get at (*cider-repl ~:localhost:46797(clj)*:52:7).
No matching method get found taking 3 args for class java.nio.file.Paths
user> (java.nio.file.Paths/get "/home" (into-array ["harold" "Downloads"]))
#object[sun.nio.fs.UnixPath 0x7e55f83d "/home/harold/Downloads"]

That's what I alluded to above -- and maybe might become easier in Clojure 1.13 (since it was considered for 1.12 but pushed off).

👍 1

> You can pass in anything that fulfills ISeq . Is that correct? If you have sequential data of any kind that you want to splat as the individual arguments when calling a function, see apply. > in (list :a :b :c) what is :a :b :c in terms of a data structure? It is not one*. The compiler does not know the arglist a function might expect (static analysis goes only so far, because vars are mutable). This is all orthogonal to the way a function may compile its received arguments into one collection using the & notation, or destructure a received list as individual parameters. The little asterisk is that platform details may vary. For example Clojurescript functions compile to Javascript functions, but Clojure functions compile to Java classes and the calling convention does involve collecting args into an array or something if there are a lot of them, like perhaps 20 or so.

the call convention for JVM clojure functions is actually a bit involved. Fixed arity functions can only ever override arities 0-20 and call into one of these: their 20+n arity will just throw. You never get to see what kind of seq was passed into a call to apply either, since their .applyTo simply tries to consume all the seq and call into a fixed arity. Variadic fns are implemented by defining a default behavior for .invoke and .applyTo that special-cases the variadic case. • the variadic .`invoke` calls into arity .getRequiredArity+1 .doInvoke, which is what the variadic fn body actually compiles to, by passing the first .getRequiredArity args and packing up the rest into an ArraySeq, or passing nil (notably, not an empty ISeq instance) if there aren't any. Note that no fixed arity is ever called on this path, since they compile to their corresponding .invoke override. • The 20+n arity .`invoke` calls into the valid .doInvoke with the last arg effectively being a list* of args getRequiredArity...20 and the array. In this case the variadic param and all its nexts will be finite instances of Cons or ArraySeq. Again, we can only enter the variadic body from here. • applyTo defaults to its usual behavior if the seq is at most .getRequiredArity long, always calling into the relevant fixed arity .invoke; otherwise the valid .doInvoke is called with its args being the first .getRequiredArity elements in the seq and then whatever's left of the seq. This is where you can see more "exotic" impls of ISeq which may even be infinite. As everyone else says, you're expected to not really care about what you actually get, much like you shouldn't care about what kind of map you get when handling an IPersistentMap.

wow, thanks a lot for that in-depth explanation @doppiaelle1999 This seems to be a more complicated topic than I anticipated.

The implementations can be pretty gnarly. Luckily, you can do a lot of Clojure in production without needing to know how the sausages are made 🙂

🎯 1