Fork me on GitHub
#clojure
<
2022-10-01
>
Ngoc Khuat13:10:19

I’m trying to use var-get to make a copy of a macro, but I don’t understand why it’s failing. It works well with function. Am I doing it wrong or there is no way to do this for macro?

👀 1
p-himik13:10:54

The problem here is that in order for something to be used as a macro, the containing var itself must be marked as a macro. def does not create a copy of a var with all the internal flags - it creates a completely new var. You can see how Clojure itself marks an existing var as a macro: (. (var defn) (setMacro)). So, try adding (.setMacro #'my-doseq) right after that def.

🙏 1
p-himik13:10:25

BTW, why do you have a need to create a "copy of a macro" in the first place?

Ngoc Khuat13:10:25

ha, that works! I wonder are there any clojure way to this? using with-meta doesn’t seem to work.

Ngoc Khuat13:10:19

I’m writing a small library that will change the implementation of a core function. And I want to be able to fall back to the original implementation.

p-himik13:10:43

That is a "clojure way" - after all, clojure.lang.Var is a part of Clojure. :) It's just that it requires interop to use it. > I’m writing a small library that will change the implementation of a core function And why is that needed, if I may ask? For some instrumentation or something else?

Ngoc Khuat13:10:16

ah I want to change the behavior a bit. Specifically I’m working on adding a new macro testing-only and intern it into clojure.test so you can use it like clojure.test/testing-only . This is meant to be used as a drop-in replacement so if a deftest has a testing-only in it, it’ll run, well only the body of testing-only and ignores the others testing . In order to do this, I need to change the implementation of clojure.test/testing a bit, so I want to preserve the original implementation so users can just opt-out if they want to.

p-himik14:10:33

But why? Why not just make a library where a user would simply have to e.g. replace the imports of clojure.test with, say, ngoc-khuat.test and be done with it? In other words, you're trying to do magic. And magic is bad. It's incomparably better to just make a user do a trivial step once in a life time of a project than to add some magic that will be absolutely impenetrable to someone who doesn't intimately know your library and that it's being used on that particular project.

p-himik14:10:21

I imagine the overall need is to be able to easily re-run a particular testing block without running the whole deftest. If that's the case, you can create your own namespace that's completely separate from clojure.test and, when you have a need to re-run a testing block, you just put a :require of that namespace under the :require vector of clojure.test with shadowing testing with a no-op macro and adding testing-only macro that just uses clojure.test/testing. This way, there's exactly 0 magic and the usage seems to be just as easy.

Ngoc Khuat14:10:33

You’re right, I don’t like magic either. But this is meant to be used as debugging tool and should not be committed. Something you just plug it into ~/.clojure/deps.edn and leave it there. My use case is I often have jumped between different tests file, and I think it’s will be convenient if you can just add -only on testing and it should just works.

Ngoc Khuat14:10:33

> If that’s the case, you can create your own namespace that’s completely separate from clojure.test I’m not following this, could you elaborate?

p-himik14:10:05

Before:

(ns my-proj.test
  (:use [clojure.test]))

(deftest hello
  (testing "a" ...)
  (testing "b" ...))
After:
(ns my-proj.test
  (:use [clojure.test] [ngoc-khuat.test]))

(deftest hello
  (testing "a" ...)
  (testing-only "b" ...))
Haven't tried it myself but I'm 80% certain that it'll work.

p-himik14:10:31

An alternative option might be to add some data reader tag that you put in front of (deftest ...) that removes all the (testing ...) blocks apart from ones marked with some other data reader tag. Just as a demonstration (not sure why the X line is printed twice though):

user=> (set! *data-readers* {`x (fn [data] (println "X" data) (concat data '(3)))})
#:user{x #object[user$eval177$fn__178 0x71178a52 "user$eval177$fn__178@71178a52"]}
user=> #user/x (+ 1 2)
X (+ 1 2)
X (+ 1 2)
6

p-himik14:10:45

Of course, in your use-case you don't want to call set! that way. Instead, you'd put a data_readers.clj file somewhere on your classpath set with ~/.clojure/deps.edn.

Ngoc Khuat14:10:38

This will not work for my case because I need to redefine the testing macro as well, and get testing already refers to #'clojure.testing error. FWIW, inorder this to affect users will have to call a function (ngockhuat.test/install!) to take affect tho.

Ngoc Khuat14:10:55

hmm, I haven’t thought about using reader, maybe I can play with that

p-himik14:10:36

> not sure why the X line is printed twice though Hmm, must be something with how the default clj REPL works. A REPL window in Cursive doesn't print the line twice. But in any case, it shouldn't affect anything.

jjttjj15:10:48

Is there a way to have clojure.edn/read-string not throw an exception upon encountering a regex literal?

jjttjj15:10:57

Thanks, I don't think that helps with reading untrusted string input though which is what I need

lread15:10:38

Yeah, cljdoc owns both writing and reading in this case.

teodorlu15:10:22

looks like edamame supports reading regexes: https://github.com/borkdude/edamame#parser-options

borkdude15:10:28

Yes, edamame is EDN + whatever you allow on top of that

Dustin Getz19:10:03

Where did the following reader implementation come from? I've been reading the source and can't seem to track it down

(defrecord Foo [x])
(tests
  (def x (->Foo 1))
  (pr-str x) := "#dustin.scratch.Foo{:x 1}"
  (read-string "#dustin.scratch.Foo{:x 1}") := x)      ; where in clojure internals was this defined?
;✅✅

1
Dustin Getz20:10:05

sym.getName().contains(".") ? readRecord(form, sym, opts, pendingForms)

👍 1
Dustin Getz20:10:15

thank you!

👍 1
Lone Ranger21:10:00

Guess I might as well ask. What is a “Ctor”?

Lone Ranger21:10:30

I know they are unhappy when I try to use undefined protocols

Dustin Getz21:10:25

I think here it just means defrecord reader

Lone Ranger21:10:36

Ctor… ConstrucTOR… I was today years old when I learned that. Thank you.

🙂 1
Dustin Getz20:10:42

This final test can never work, right? (for safety i guess) Why is global tag registration allowed in CLJS then?

(tests
  "#uri literals in clojure runtime readers are auto-wired from data_readers.cljc (unsafe)"
  #?@(:clj ((clojure.core/read-string "#uri \"\"") := x))

  "#uri literals are not auto-wired in cljs reader (it uses JS runtime and therefore is an EDN reader for safety)"
  ; 
  #?@(:cljs ((cljs.reader/read-string "#uri \"\"")
             :throws js/Error ; No reader function for tag uri.
             (contains? (set (keys @cljs.reader/*tag-table*)) 'inst) := true
             (contains? (set (keys @cljs.reader/*tag-table*)) 'uri) := false))
  
  "cljs userland can globally register an EDN tag reader, but it's probably a bad idea"
  #?@(:cljs ((cljs.reader/register-tag-parser! 'uri ->URI)
             (clojure.edn/read-string "#uri \"\"") := x))
  
  "Clojure userland can not globally register an EDN tag reader"
  #?@(:clj ((clojure.edn/read-string "#uri \"\"") :throws RuntimeException)))

Dustin Getz22:10:30

HOWTO install #uri reader extension in Clojure/Script Can I get a code review on this please? It was hard fought knowledge (this is my third time in 6 years going through this exercise and the first time I think i fully understand it) every clojure project eventually needs this so I would like to put it somewhere where folks can find it – perhaps ask clojure? where should I put it? https://gist.github.com/dustingetz/89a8333b8e5bf73b0f527d865d53079a

seancorfield22:10:24

I thought Clojure reserved all unqualified reader extension names for itself? (so users should always qualify reader extension names)

seancorfield22:10:13

Can I just say of "every clojure project eventually needs this" that I've never needed this in over a decade of Clojure production work? 🙂

🙂 2
Dustin Getz22:10:28

Yes that is true, IMO this should be in core (http://java.net.URI and transit URI are not my types); this is why we chose an unqualified tag we think this should be in core, in alignment with clojure.core/uri? being hardcoded to java.net.URI and goog.Uri, they are not userland types

borkdude16:10:27

I still would go with a qualified tag even if you don't own the type: this makes it possible for folks to find out where this comes from and won't override a hypothetical future built-in implementation

Dustin Getz16:10:07

if a builtin is provided this impl becomes superseded and should be removed

Dustin Getz16:10:26

this is also likely the exact impl that core would provide

borkdude16:10:39

The problem is that you can't easily undo overriding built-in tags if people have data_readers.cljc stuff on their classpath that override

1
Dustin Getz16:10:35

Ah so if this extension ends up bundled as a transitive dependency in some lib

borkdude16:10:56

Yes. So even if you wish to remove it, as you know, software isn't killed that easily

Dustin Getz16:10:41

thanks, good argument