Fork me on GitHub
#clojure
<
2023-09-14
>
Noah Bogart02:09:01

i have a process that's doing some io and prints some updates to stdout. i'd like to parallelize the process and keep the printing to stdout, but avoid interleaving the printing. what's the best way to do this?

hiredman02:09:42

Depends on what you mean by interleaving, but generally, printing a single string to stdout is atomic

👍 1
hiredman02:09:23

A printf first interpolates the values, then prints the resulting single string

Noah Bogart02:09:22

okay, so building up a printable string (stringbuilder) and then printing once will keep things together. thank you, appreciate it

mdiin04:09:25

Would it work for your use case to add-tap println and then use tap> instead of println? I think that will prevent interleaved prints when things run concurrently.

Hendrik08:09:15

I use a library that requires ns datomic.client.api. I want this to be resolved as ns datomic.pro.api. Is it possible to mock a namespace require? I want to use the library without having datomic.client.api on classpath

p-himik08:09:28

You can create a namespace that has everything that datomic.pro.api has. Can be done either at run time (but you have to make sure it happens before the library that requires it is loaded) or at dev time by creating a separate file in your own sources.

Hendrik08:09:56

Thanks. Makes totally sense and a really clean comprehensible solution. 🙂 Why was I even thinking of using mocking tools? 😄

joshcho11:09:44

What are the best resources for advanced macros techniques? I am reading the Macro section of Joy of Clojure for now.

rutledgepaulv11:09:43

I found https://letoverlambda.com/ pretty interesting

❤️ 4
dpsutton12:09:24

i think reading zach tellman code to find some advanced macros is very handy

joshcho12:09:01

@U11BV7MTK do u have anything by zach tellman in particular?

Noah Bogart12:09:08

Let Over Lambda is good, but the author and writing voice are extremely “smug lisp weenie”, specifically about Common Lisp. It’s sometimes hard to read because if it, but if you can stomach it, the demonstrated macros are quite cool

👍 3
kennytilton15:09:30

https://github.com/kennytilton/flutter-mx and https://github.com/kennytilton/web-mx have macros, macros that write macros, and most macros leverage lexical capture. Let me know if you have questions!

❤️ 1
joshcho16:09:11

@U0PUGPSFR I guess first and foremost, I'm curious about designing the language for the problem you're trying to solve. I think this is extremely fascinating and trying to gauge different DSLs that people have invented for the particular problem at hand might be really useful, especially if that DSL is in Clojure. Perhaps less macro techniques, but more language design? Case studies on how language design reduces incidental complexity.

kennytilton19:09:49

Ah, @U6D1FU8F2, I see, it is the language design itself of the DSL that interests you. Good focus! And I have to say, I have seen macro-based DSLs that were hard to approach, because the DSL itself turned out poorly (IMO). Not sure what to say. I get frustrated while programming (which is how I found Lisp). This applies to languages/DSLs I create, of course, so I think my language design process is just "If I do not like it, make it better." 🙂 Have you looked at Paul Graham's http://www.paulgraham.com/onlisp.html? Quite a good read on macrology, in a Common Lisp context.

joshcho23:09:26

@U0PUGPSFR Makes sense. I skimmed On Lisp a couple years ago, but probably worthwhile to go back to. I am mostly writing a DSL for a small team, and maybe with enough practice in DSL design, I can express more concisely and elegantly.

phill00:09:48

Colin Jones' book, "Mastering Clojure Macros"

❤️ 2
phill00:09:05

re the reference to "Let over Lambda"... It is a terrific work of literature, a riot actually, a life-enriching read; but it is basically not very much help on the question of Clojure macros. E.g., as I recall, "Let over lambda" rhapsodizes about special effects courtesy of Lisp-2, while Clojure is a Lisp-1.

tatut08:09:15

core.async has the go macro which is quite complicated

Bobbi Towers10:09:11

I've been inhaling Mastering Clojure Macros all day because it's just the text I've been wishing for. I'd never written a macro before but now I'm in the deep end because I'm building a Clojure interpreter. I recommend chapter 6, Build APIs That Say Just What They Mean, in which the author gives a tour of Compojure by James Reeves. Hiccup is also amazing, a shining model of a DSL.

❤️ 1
tatut10:09:18

afaict hiccup is “just data” representation of a DOM tree

tatut10:09:38

which Clojure excels at

dvingo11:09:10

Tim Baldridge had some great content on Pivotshare but sadly looks like the platform is defunct :( he has some of the content on youtube though https://www.youtube.com/watch?v=HXfDK1OYpco https://www.reddit.com/r/Clojure/comments/10h5pha/tim_baldridges_clojure_tutorials/

❤️ 1
Bobbi Towers11:09:15

OMG he has like 2 hours of video on the go macro

dvingo11:09:44

if anyone is in contact with him would be awesome to get the pivotshare videos hosted someplace else

jahson11:09:56

The most advanced technique is not to use macros at all IMO 🤷

practicalli-johnny12:09:05

First rule of Clojure Macro Club... don't write macros (unless they provide significant value and are worth the maintenance)

reefersleep13:09:02

It's fun to practice, though 🙂 And practising can give you insight that can help you understand other people's code.

jahson11:09:59

Or use them very sparingly.

flefik17:09:34

I have a large-ish collection of long strings. I need to check if any elements of a large-ish set #{"foo" ,,, "bar"} is a substring. If there are any matches return a truthy value. Is there a simple idiomatic way to do this in clojure that isn’t O(n^2)? (It doesn’t have to be optimal like Aho Corasick)

Joshua Suskalo18:09:48

Collect all the elements of the set into a regex using alternation. If you have no lookahead/lookbehind and you're looking for a substring in any of the long strings then you're looking at O(m*n) where m is the number of long strings and n is the maximum length of the long strings. This is much better than directly using the set and repeatedly checking each string if it contains each target substring, as compiled regular grammars (which this subset of regex is) have linear match time.

2
flefik18:09:31

Thank you!

flefik18:09:48

Like so @U5NCUG8NR ?

(re-find
  (->>
    #{"foo" "bar" "baz" ,,,}
    (clojure.string/join "|")
    re-pattern)
  "my long fooo string")

Joshua Suskalo18:09:05

Yeah, although I'd recommend also throwing in a regex quote on them

Joshua Suskalo18:09:44

Also be sure to not repeatedly call re-pattern if you can help it, as constructing an optimal regex VSM is an O(n^2) operation, where n is the number of test strings.

flefik18:09:03

My set is static fortunately

Joshua Suskalo18:09:08

But yeah, throw in a (map #(java.util.regex.Pattern/quote %)) into that thread last chain before the join

Joshua Suskalo18:09:22

that way if your set has any special regex characters they will be matched literally instead of interpreted as a regex.

flefik18:09:37

Good idea! (although they’re just English words in this case)

oyakushev20:09:16

In a more general case, I would build a trie, but a regexp does something similar under the hood and is indeed a very elegant solution.

Joshua Suskalo20:09:03

Yeah, with an alternation in this case it does (approximately) the same thing, and it doesn't require building a trie yourself or pulling in a library.

👍 1
oyakushev20:09:21

Out of curiosity, I made a barebones trie version to have a comparison. The difference in performance is ~50 times in my benchmark. So in cases where the performance is important (and the size of the dictionary is very large), trie is still a king.

(def words (->> (str/split-lines (slurp "/usr/share/dict/words"))
                (random-sample 0.01)
                (mapv #(str/replace (str/lower-case %) #"[^a-z]" ""))))

;;;; Regexp version

(def rx (re-pattern (str/join "|" words)))

(crit/quick-bench (re-matches rx (rand-nth words)))
;; Execution time mean : 4.813902 µs

;;;; Trie version

(defn make-trie []
  (object-array (* 26 2)))

(defn fill-trie [^objects trie, ^String word, i]
  (let [c (.charAt word i)
        pos (* (- (int c) (int \a)) 2)]
    (if (= i (- (.length word) 1))
      (aset trie (inc pos) true)
      (do (when (nil? (aget trie pos))
            (aset trie pos (make-trie)))
          (recur (aget trie pos) word (inc i))))))

(defn find-in-trie [^objects trie, ^String word, i]
  (let [c (.charAt word i)
        pos (* (- (int c) (int \a)) 2)
        next (aget trie pos)]
    (cond (= i (- (.length word) 1)) (some? (aget trie (inc pos)))
          (nil? next) false
          :else (recur next word (inc i)))))

(def top (make-trie))

(run! #(fill-trie top % 0) words)

(crit/quick-bench (find-in-trie top (rand-nth words) 0))
;; Execution time mean : 103.151610 ns

👍 1
vemv21:09:34

While I'm too sleepy to check if it would solve the problem in any way, https://github.com/noprompt/frak came to mind Very cool lib.

❤️ 1
Felix Dorner21:09:56

In javadoc I can mention other methods via “`link`” , (can’t remember the exact syntax). Do clojure docstrings have similar convention?

seancorfield22:09:35

No, Clojure docstrings are plain text. However, if you use specific doc-generator tools, such as http://cljdoc.org, those have conventions for using Markdown in docstrings and some of them support links to functions, namespaces, etc.

dpsutton22:09:56

cider supports references of the shape "docstring supports links to [[vars]]" and will navigate to the [[var]] reference i believe

octahedrion06:09:45

could put it in the meta like {:link #'other-fn :doc "..."}

Felix Dorner07:09:07

Wikilinks are what I was looking for, thanks!