Fork me on GitHub
#beginners
<
2023-12-29
>
Jason Bullers16:12:24

In the https://clojure.org/guides/spec of using Clojure spec, the deck (among other things) is shown as

(s/def :game/deck (s/* :game/card))
Is there a reason why this can't/shouldn't be
(s/def :game/deck (s/coll-of :game/card))
What's the difference and what heuristics would I use to choose one over the other?

Alex Miller (Clojure team)17:12:01

They are subtly different. * is a regex op and will combine with other regex ops to form a single collection, whereas coll is not

Alex Miller (Clojure team)17:12:26

Let me look at the guide to recall the full example here

Alex Miller (Clojure team)17:12:38

I wrote this, but I don’t recall why I used * instead of coll here. I think I would probably reach for coll first now. Might be some difference in generated values, but I think should be pretty similar either way.

Bob B17:12:14

The guide does call out in the collections section that the 'point' example can be done with s/cat or s/coll-of or s/tuple, but the options conform differently and are differently nestable, and I believe roughly the same idea applies here.

Alex Miller (Clojure team)17:12:26

Generally you will want the regex ops when you are describing a collection with internal structure, most commonly syntactic structure for a macro signature

Alex Miller (Clojure team)17:12:46

And for “information”/data use cases (like this one), I would probably use coll-of.

👍 1
Jason Bullers19:12:25

If I want to break my specs up in multiple namespaces, is it sensible to just require the namespaces that have specs the current namespace depends on? For example, if I have a ns called player and another called game:

;; ns player
(s/def :player/name string?)
(s/def :player/score int?)
(s/def :player/player (s/keys :req [:player/name :player/score]))

;; ns game 
(s/def :game/player (s/coll-of :player/player))
Do I just (require [player]) in game even if I don't have any functions in that ns to call? Is it preferable to dump all the specs in the same namespace? It "feels" more right to separate them by domain concept, but then of course there will be interconnections. Does spec choke on cycles in general? What about when multiple namespaces are involved?

Alex Miller (Clojure team)20:12:21

I have found generally it’s better to put the “domain” specs in their own package. In most cases, specs can refer to other specs by name without worrying about loading order (as long as they are all loaded before use). You can use :require with :as-alias to get name aliasing without loading if needed

Omer ZAK20:12:49

What is the Clojure's equivalent to Python's subprocess package? In a POSIX system I want to invoke, from a Clojure script, a binary, feed data into its stdin, and retrieve its outputs from stdout and stderr. I want also to get the status of its running. Such a subprocess equivalent would enable me to do all the above.

Alex Miller (Clojure team)20:12:10

clojure.java.shell is probably the closest in 1.11.1. Clojure 1.12 will have a new clojure.java.process namespace that’s updated a bit

Alex Miller (Clojure team)20:12:24

There are some external libs as well, notably https://github.com/babashka/process

Alex Miller (Clojure team)20:12:49

The new c.j.process has a lot of overlap with that

Omer ZAK21:12:14

I tried to :require clojure.java.shell and got Syntax error (ClassNotFoundException) Was I supposed to capitalize anything or do some other magic?

Alex Miller (Clojure team)22:12:44

user=> (require '[clojure.java.shell :as shell])
nil
user=> (dir shell)
*sh-dir*
*sh-env*
sh
with-sh-dir
with-sh-env
nil

Alex Miller (Clojure team)22:12:06

or if you're in the ns form, you'd use (:require [clojure.java.shell :as shell])

Omer ZAK22:12:17

Thanks, I see I had to quote the vector given to require (I know what quoting a list means, but what does quoting a vector mean?). And (println (shell/sh "ls" "-al" "nonexistent")) demonstrated to me that I get also stderr and status code.

Alex Miller (Clojure team)23:12:21

Quoting (in all cases) means “read but don’t evaluate”. Vectors evaluate their elements, and symbols are resolved to the value they refer to. In this case quoting is used to treat the symbols as symbols, without resolving them