Fork me on GitHub
#clojure
<
2022-09-17
>
pavlosmelissinos00:09:34

I want to expose certain transitive symbols to the users of my namespace, as a convenience, so that they don't have to require the namespaces themselves. However, def loses all the metadata and signatures of the original symbol which is worse in my case. alter-meta! works, but I'd like to understand why with-meta doesn't. Code sample in 🧵

pavlosmelissinos00:09:51

(require '[ :as io])

(def file io/file)
(alter-meta! #'file merge (meta #'io/file))

(def file2 (with-meta io/file (meta #'io/file)))

(meta #'file)
;;=> {:name file, :ns #object[sci.lang.Namespace 0x213f8d58 ""], :file "...", :arglists ([arg] [parent child] [parent child & more]), :doc "Returns a java.io.File, passing each arg to as-file.  Multiple-arg\n   versions treat the first argument as parent and subsequent args as\n   children relative to the parent."}

(meta #'file2)
;;=> {:name file2, :ns #object[sci.lang.Namespace 0x193ce344 "..."], :file "..."}

pppaul00:09:07

there are a few libraries that provide namespace helpers that do def aliasing, and some other things that you may find useful

p-himik00:09:42

Assuming I understand your desire correctly, there's also https://github.com/clj-commons/potemkin. But my perception of the community as a whole is that such practices are generally discouraged. If you want to put something in some specific namespace - let it reside there and let the users know where to find those things.

pavlosmelissinos00:09:06

I understand this is a dangerous path but it's mostly educational for me, so it's ok if it's discouraged 🙂 Thank you both, so the short answer is I need to write a macro to make with-meta work? If that's the case, alter-meta! is good enough for me, I don't want to complicate it further. As a matter of fact, the code in the links helped me realize that I can rewrite my first example as a single expression: (alter-meta! (def file io/file) merge (meta #'io/file))

chrisn01:09:26

That sort of definition by macro will make code completion fail for some environments. For this reason if we use macros of any sort to define an api we take a second step to ‘export’ via ns-publics this api to yet another vanilla api that calls into the first - https://github.com/cnuernber/dtype-next/blob/master/src/tech/v3/datatype/export_symbols.clj

rolt08:09:30

your initial confusion is due to the difference of vars and values in the first case you're assigning the meta to the var, in the second case to the value of the var

ChillPillzKillzBillz12:09:31

How to define symbols in global scope from inside a let construct?

borkdude12:09:25

@abhishek.mazy

(let [foo 1]
  (def x foo))

ChillPillzKillzBillz12:09:34

Thanks for that @borkdude! I am really struggling with a simple issue. I first posted the query in the beginners thread https://clojurians.slack.com/archives/C053AK3F9/p1662978387604509 I have changed some of the approach since that thread... Before the code here is my deps listing

{:paths ["src"]
 :deps {org.clojure/clojurescript {:mvn/version "1.10.866"}
        sicmutils/sicmutils {:mvn/version "0.20.0"}
        thheller/shadow-cljs {:mvn/version "2.14.4"}
        io.github.nextjournal/clerk {:mvn/version "0.9.513"}
        com.github.jpmonettas/flow-storm-dbg {:mvn/version "RELEASE"}
        com.github.jpmonettas/flow-storm-inst {:mvn/version "RELEASE"}}}
I am trying out sicmutils and trying out some math functions. In order to do what I need to do, I need to define named variables which are sicmutils literal-functions. So for clarity, lets assume I've got a list of labels as follows:
position_labels
["theta_Diff" "theta_Splt" "theta_L" "theta_R"]
I am trying to write a macro but before the macro listing I wanted to see if the code that I generate in the macro makes sense... so here is my attempt
(-> (reduce #(conj %1 (list 'def
                            (symbol %2)
                            (list 'e/literal-function
                                  (list 'symbol
                                        %2))))
            ()
            position_labels)
    (conj 'do))
This generates the following
(do
 (def theta_R (e/literal-function (symbol "theta_R")))
 (def theta_L (e/literal-function (symbol "theta_L")))
 (def theta_Splt (e/literal-function (symbol "theta_Splt")))
 (def theta_Diff (e/literal-function (symbol "theta_Diff"))))
When I execute this generated code it behaves as desired, i.e. create variables like theta_R at global scope However when I create a macro out of identical code...
(defmacro def-literal-functions [labels]
  (-> (reduce #(conj %1 (list 'def
                              (symbol %2)
                              (list 'e/literal-function
                                    (list 'symbol
                                          %2))))
              ()
              labels)
      (conj 'do)))
The macro compiles ok, however the following execution gives me the error as follows
(def-literal-functions position_labels)
; Syntax error macroexpanding def-literal-functions at (..\sicmutils-main\demo\.calva\output-window\output.calva-repl:245:1).
; Don't know how to create ISeq from: clojure.lang.Symbol
So what is going on? Why can't I get this macro to do what the code does? macroexpand doesn't work because I get the same error message.

borkdude12:09:39

maybe print out the macroexpansion before it expands?

ChillPillzKillzBillz12:09:50

how do I do that? I am still very new to Clojure

borkdude12:09:48

(defmacro foo []
  (let [expansion `(do ...)]
    (prn expansion)
    expansion))

👍 1
ChillPillzKillzBillz13:09:18

I had no idea!! Let me give that a try

borkdude13:09:14

are you using the macro from CLJS?

borkdude13:09:45

in this case it might be safer to do something like:

(.println System/err (str expansion))
since CLJS uses the out stream to emit code which can sometimes be confusing

ChillPillzKillzBillz13:09:22

not at the moment... just CLJ... just eventually I'll use CLJS

ChillPillzKillzBillz13:09:33

lets see if this solves the issue

ChillPillzKillzBillz13:09:29

ok new code

(defmacro def-literal-functions [labels]
  (let [expansion '(-> (reduce #(conj %1 (list 'def
                                          (symbol %2)
                                          (list 'e/literal-function
                                                (list 'symbol
                                                      %2))))
                          ()
                          labels)
                  (conj 'do))]
    (prn expansion)
    expansion))
which gives
(def-literal-functions position_labels)
(-> (reduce 
     (fn* [p1__61045# p2__61046#] 
          (conj p1__61045# (list (quote def) 
                                 (symbol p2__61046#) 
                                 (list (quote e/literal-function) 
                                       (list (quote symbol) p2__61046#)))))
     () 
     labels) 
    (conj (quote do)))
; Syntax error compiling at (h:\Work\Clojure\sicmutils-main\demo\.calva\output-window\output.calva-repl:270:24).
; Unable to resolve symbol: labels in this context
So it is unhappy with labels... that gets passed in the reduce. Inspite of labels being one of the arguments in the macro definition...? Why?

borkdude13:09:41

You need to unquote it like this: ~labels

ChillPillzKillzBillz13:09:11

New code

(defmacro def-literal-functions [labels]
  (let [expansion '(-> (reduce (fn [result label]
                                 (conj result (list 'def
                                                    (symbol label)
                                                    (list 'e/literal-function
                                                          (list 'symbol
                                                                label)))))
                               ()
                               ~labels)
                       (conj 'do))]
    (prn expansion)
    expansion))
Which gives
(def-literal-functions position_labels)
(-> (reduce (fn [result label]
              (conj result (list (quote def)
                                 (symbol label)
                                 (list (quote e/literal-function)
                                       (list (quote symbol) label)))))
            ()
            (clojure.core/unquote labels))
    (conj (quote do)))
; Syntax error compiling at (h:\Work\Clojure\sicmutils-main\demo\.calva\output-window\output.calva-repl:270:24).
; Unable to resolve symbol: labels in this context
What is going on? 😄

borkdude13:09:00

you need to use

` instead of ' for quoting

borkdude13:09:25

this is called the backtick which is typically used in macro expansions

borkdude13:09:44

no, the whole quoted expression

borkdude13:09:41

wait, why did you quote the expression

borkdude13:09:55

undo that and also remove the unquote before labels

ChillPillzKillzBillz13:09:21

as was the case in your code... sorry if I misunderstood

ChillPillzKillzBillz13:09:37

I see that you were using ` instead of '...

borkdude13:09:44

so write (let [expansion (-> (reduce ...))] (prn expansion))

borkdude13:09:57

you have written the expansion in such a style that you don't need the backquote

borkdude13:09:02

I didn't notice that before

ChillPillzKillzBillz13:09:11

so something like as follows:

(defmacro def-literal-functions [labels]
  (let [expansion (-> (reduce (fn [result label]
                                (conj result (list 'def
                                                   (symbol label)
                                                   (list 'e/literal-function
                                                         (list 'symbol
                                                               label)))))
                              ()
                              ~labels)
                      (conj 'do))]
    (prn expansion)
    expansion))
which when run gives:
(def-literal-functions position_labels)
; Syntax error macroexpanding def-literal-functions at (h:\Work\Clojure\sicmutils-main\demo\.calva\output-window\output.calva-repl:366:1).
; Attempting to call unbound fn: #'clojure.core/unquote

ChillPillzKillzBillz13:09:27

the expansion doesn't print anything anymore

borkdude13:09:47

yes, without the ~ as well

ChillPillzKillzBillz13:09:32

New code

(defmacro def-literal-functions [labels]
  (let [expansion (-> (reduce (fn [result label]
                                (conj result (list 'def
                                                   (symbol label)
                                                   (list 'e/literal-function
                                                         (list 'symbol
                                                               label)))))
                              ()
                              labels)
                      (conj 'do))]
    (prn 'expansion)
    expansion))
Which gives
(def-literal-functions position_labels)
; Syntax error macroexpanding def-literal-functions at (h:\Work\Clojure\sicmutils-main\demo\.calva\output-window\output.calva-repl:378:1).
; Don't know how to create ISeq from: clojure.lang.Symbol

borkdude13:09:27

do (prn expansion) without the quote

borkdude13:09:26

I think you intend to write [& labels]

borkdude13:09:35

as the arguments of the macro

ChillPillzKillzBillz13:09:36

This completely changes the expansion of the list New code

(defmacro def-literal-functions [& labels]
  (let [expansion (-> (reduce (fn [result label]
                                (conj result (list 'def
                                                   (symbol label)
                                                   (list 'e/literal-function
                                                         (list 'symbol
                                                               label)))))
                              ()
                              labels)
                      (conj 'do))]
    (prn expansion)
    expansion))
Which when run gives
(def-literal-functions position_labels)
(do (def position_labels (e/literal-function (symbol position_labels))))
; Execution error (IllegalArgumentException) at nrepl.middleware.interruptible-eval/evaluate$fn$fn (interruptible_eval.clj:87).
; no conversion to symbol
This is understood as symbol doesn't like to have a list for an argument... what is the best way to expand a list in a macro? FYI
position_labels
["theta_Diff" "theta_Splt" "theta_L" "theta_R"]

borkdude13:09:58

@abhishek.mazy I think you're better off just extracting a defn , feeding that the literal data you're passing to the macro otherwise and then running that function in the REPL. When you're done, use that function in the macro

borkdude14:09:03

this helps you iterate faster

borkdude14:09:12

it's just data transformation after all

ChillPillzKillzBillz14:09:13

can you do a def from within a defn? and still have the symbols defined in global scope?

borkdude14:09:36

I mean, pull the reduce expression into a defn:

(defn foo [& labels] (-> (reduce ...))
And then test it with:
(foo 'dude 'foo)

borkdude14:09:44

and then see what it constructs

ChillPillzKillzBillz14:09:56

thanks I'll give that a go

ChillPillzKillzBillz14:09:01

sorry for so many questions

ChillPillzKillzBillz14:09:58

If you still have the energy to look into this... the function just generates the code... correctly but doesn't execute it...

(defn tryfn [labels]
  (let [expansion (-> (reduce (fn [result label]
                                (conj result (list 'def
                                                   (symbol label)
                                                   (list 'e/literal-function
                                                         (list 'symbol
                                                               label)))))
                              ()
                              labels)
                      (conj 'do))]
    (prn expansion)
    expansion))
when run it gives
(tryfn position_labels)
(do (def theta_R (e/literal-function (symbol "theta_R"))) (def theta_L (e/literal-function (symbol "theta_L"))) (def theta_Splt (e/literal-function (symbol "theta_Splt"))) (def theta_Diff (e/literal-function (symbol "theta_Diff"))))
(do
 (def theta_R (e/literal-function (symbol "theta_R")))
 (def theta_L (e/literal-function (symbol "theta_L")))
 (def theta_Splt (e/literal-function (symbol "theta_Splt")))
 (def theta_Diff (e/literal-function (symbol "theta_Diff"))))
How do I execute the term expansion?

ChillPillzKillzBillz14:09:18

solved it!! I am using eval!! If you have better idea... please let me know... I am grateful for your help! Many thanks!

borkdude14:09:59

you could do this without eval using defmacro - this should only be a small step now

borkdude14:09:25

(defmacro foo [& labels] (tryfn labels))

borkdude14:09:29

and then remove eval

ChillPillzKillzBillz14:09:43

ah ok... let me try that

ChillPillzKillzBillz14:09:07

Ok so last word on this... for macros... I can't seem to supply a list... I can only make it work if I provide a macro with an expanded value of the list... the macro will not expand the list inside a defmacro... By this I mean if we have

position_labels 
["x" "y" "z"]
Then in order to get the macro to pass, we can't supply
(def-literal-func-macro position_labels)
instead we have to pass
(def-literal-func-macro "x" "y" "z")
is there ANY way to get around this?

borkdude14:09:06

no :) macros receive compile time values, so when the argument is a symbol it stays a symbol

borkdude14:09:29

e.g. you can't do this either:

(let [var x] (defn var [] "hello"))

ChillPillzKillzBillz14:09:39

ok for my purposes, I'll stick to function... I learnt a lot in generating custom code and finally executing it over the last couple of weeks... Many thanks for your help @borkdude!!

👍 1
Dallas Surewood17:09:27

Has anyone done anything with operational transformation in clojure before? I've only seen small libraries on the topic

thegeez10:09:23

I've used operation transformations in Clojure and did a talk about it at EuroClojure: http://thegeez.net/2017/08/17/crepl_euroclojure_2017_berlin.html

borkdude18:09:52

@sdata374 @thegeez has done some projects with that and also has given talks on those (at least one). Maybe he could tell you more.

jpmonettas19:09:34

Any ideas on how is this conflict happening?

clj -Sforce -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.11.60"} cnuernber/dtype-next {:mvn/version "9.032"}}}'

Clojure 1.11.1
user=> (require '[cljs.repl :as cljs-repl])

Execution error (NoSuchMethodError) at com.google.javascript.jscomp.RhinoErrorReporter/<clinit> (RhinoErrorReporter.java:169).
'com.google.common.collect.ImmutableMap com.google.common.collect.ImmutableMap$Builder.buildOrThrow()'

jpmonettas19:09:08

One step further this is the dtype-next dependency that is causing the conflict :

clj -Sforce -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.11.57"} com.google.guava/guava {:mvn/version "30.1.1-jre"}}}'
so clojurescript conflicts with guava, but I don't understand why

jpmonettas19:09:02

guava sub dependencies don't seems to be causing the problem

jpmonettas19:09:44

hmmm works with the latest guava version "31.1-jre" :man-shrugging:

pooriaTaj19:09:15

‍‍

Hi I am getting an exception like this , I have some unclear guesses why this is happening but i am using a shared ref for mutable data storage in memory and face this bulk of exceptions
ncaught exception << java.lang.IllegalStateException: Attempting to call unbound fn: #'io.aviso.exception/update-keys >> on thread << async-thread-macro-7 >> -- with data << nil >>  -- Full Info : << {:via [{:type java.lang.IllegalStateException, :message "Attempting to call unbound fn: #'io.aviso.exception/update-keys", :at [clojure.lang.Var$Unbound throwArity "Var.java" 45]}], :trace [[clojure.lang.Var$Unbound throwArity "Var.java" 45] [clojure.lang.AFn invoke "AFn.java" 36] [io.aviso.exception$write_exception_STAR_$write_exception_stack__8510 invoke "exception.clj" 542] [io.aviso.exception$write_exception_STAR_ invokeStatic "exception.clj" 563] [io.aviso.exception$write_exception_STAR_ invoke "exception.clj" 520] [io.aviso.exception$write_exception invokeStatic "exception.clj" 630] [io.aviso.exception$write_exception invoke "exception.clj" 565] [io.aviso.exception$format_exception invokeStatic "exception.clj" 637] [io.aviso.exception$format_exception invoke "exception.clj" 632] [io.aviso.exception$format_exception invokeStatic "exception.clj" 635] [io.aviso.exception$format_exception invoke "exception.clj" 632] [taoensso.timbre$stacktrace invokeStatic "timbre.cljc" 912] [taoensso.timbre$stacktrace invoke "timbre.cljc" 896] [taoensso.timbre$default_output_fn invokeStatic "timbre.cljc" 53] [taoensso.timbre$default_output_fn invoke "timbre.cljc" 37] [taoensso.timbre$default_output_fn invokeStatic "timbre.cljc" 40] [taoensso.timbre$default_output_fn invoke "timbre.cljc" 37] [taoensso.encore$fmemoize$fn__9510$fn__9513 invoke "encore.cljc" 2086] [clojure.lang.Delay deref "Delay.java" 42] [clojure.core$deref invokeStatic "core.clj" 2337] [clojure.core$deref invoke "core.clj" 2323] [taoensso.encore$fmemoize$fn__9510 invoke "encore.cljc" 2074] [taoensso.timbre$_log_BANG_$fn__10451$fn__10453 invoke "timbre.cljc" 640] [clojure.lang.Delay deref "Delay.java" 42] [clojure.lang.Delay force "Delay.java" 28] [clojure.core$force invokeStatic "core.clj" 767] [clojure.core$force invoke "core.clj" 763] [taoensso.timbre.appenders.core$println_appender$fn__10273 invoke "core.cljc" 68] [taoensso.timbre$_log_BANG_$fn__10451 invoke "timbre.cljc" 668] [clojure.lang.PersistentArrayMap kvreduce "PersistentArrayMap.java" 429] [clojure.core$fn__8525 invokeStatic "core.clj" 6908] [clojure.core$fn__8525 invoke "core.clj" 6888] [clojure.core.protocols$fn__8257$G__8252__8266 invoke "protocols.clj" 175] [clojure.core$reduce_kv invokeStatic "core.clj" 6919] [clojure.core$reduce_kv invoke "core.clj" 6910] [taoensso.timbre$_log_BANG_ invokeStatic "timbre.cljc" 612] [taoensso.timbre$_log_BANG_ invoke "timbre.cljc" 526] [hermes.van_buren.sauron.instance_watcher.core$dispatch_observation$fn__14299$fn__14300 invoke "core.clj" 216] [hermes.van_buren.sauron.instance_watcher.core$dispatch_observation$fn__14299 invoke "core.clj" 127] [clojure.core.async$thread_call$fn__5903 invoke "async.clj" 484] [clojure.lang.AFn run "AFn.java" 22] [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1128] [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 628] [java.lang.Thread run "Thread.java" 829]], :cause "Attempting to call unbound fn: #'io.aviso.exception/update-keys"} >>

hiredman19:09:14

This is actually two problems, something is throwing an exception, then the pretty printer for exceptions timbre uses called aviso is throwing an exception while printing the exception

hiredman20:09:43

Looks like the error printing the exception is this https://github.com/ptaoussanis/timbre/issues/344

pooriaTaj20:09:38

Thanks for your reply

pooriaTaj20:09:54

Actually I figured out the problem with timbre printing it

pooriaTaj20:09:29

I am looking for the second problem's sourcre , which is a run time exception. Any Idea how debugging such an issue is possible ?

pooriaTaj20:09:06

I actually figured out what the problem was : Your advice helped , I rolled my clojure version back to 1.10.3 because i could not make timbre not take it's default dependency for some reason. Then I Found out the bug was actually because of a jdbc connection I had , the stack trace was not printing correctly and the error shown was totally irreleveant to the real problem .

💕 1