Fork me on GitHub
#clojure
<
2019-10-21
>
Karol Wójcik10:10:19

Hi. I found hard to quote the fully qualified symbol in macro. 1. To macro I'm passing an arg (symbol "view.core" (name asym)) 2. When I do ~arg then I got a symbol: view.core/importer 3. In macro I would like to quote it to 'view.core/importer I tried something like `(quote ~arg) but the effect is '(symbol "view.core" (name asym)) Any help is greatly appreciated

vlaaad10:10:45

(defmacro boop [sym]
  `'~(symbol "view.core" (name sym)))

(boop beep) => view.core/beep
@karol.wojcik like that?

Karol Wójcik10:10:52

@U47G49KHQ

(defmacro qualify
  [asym]
  `'~asym)

(defmacro resolve*
  [module fasym args]
  `(when-not core.util/nodejs?
     (println "ARGS" ~args)
     (println "FASYM" (qualify ~fasym))
     (cljs.loader/load ~module
                       (fn []
                         (println ~fasym)
                         ;; ((cljs.core/resolve (quote ~fasym)))
                         ))))
Still having (symbol "view.core" (name sym))

vlaaad10:10:14

it's not clear to me what do you want to achieve

vlaaad10:10:32

is "view.core" another arg?

vlaaad10:10:37

module? or what?

vlaaad10:10:26

(defmacro resolve*
  [module fasym args]
  (let [qualified-sym (symbol module (name fasym))]
    `(when-not core.util/nodejs?
       (println "ARGS" ~args)
       (println "FASYM" '~qualified-sym)
       (cljs.loader/load ~module
                         (fn []
                           (println '~qualified-sym))))))
something like that?

vlaaad10:10:47

and (macroexpand '(resolve* "view.core" boop nil)) will have that: (println "FASYM" (quote view.core/boop))

vlaaad10:10:03

which will print view.core/boop at runtime

Karol Wójcik10:10:58

fasym is that (symbol "view.core" "importer")

Karol Wójcik11:10:16

I want to have (quote view.core/importer)

Karol Wójcik11:10:49

But (symbol "view.core" "importer") is an argument passed to resolve*

vlaaad11:10:43

can you show example args to resolve*?

vlaaad11:10:38

is it like (resolve* "module??" (symbol "view.core" "importer") some-args???)?

Karol Wójcik11:10:51

(core.lazy/resolve* module (symbol "view.core" (name asym)) args) Where module is :view, args are nil, asym is 'importer

vlaaad11:10:33

not sure it can work that way, when you pass (symbol "view.core" (name asym)) to a macro, it won't receive a symbol, it will receive a list, where first element is symbol symbol, second is a string "view.core" etc.

vlaaad11:10:49

are you sure you need it as a macro?

vlaaad11:10:22

well you can eval fasym arg and store it in let:

(defmacro resolve*
  [module fasym args]
  `(let [qualified# ~fasym]
     (when-not core.util/nodejs?
       (println "ARGS" ~args)
       (println "FASYM" qualified#)
       (cljs.loader/load ~module
                         (fn []
                           (println qualified#))))))

vlaaad11:10:54

but at this point there is no difference between this macro and a regular defn

vlaaad11:10:00

it would be great if you described the problem you are trying to solve with this macro. what are args? what should happen at compile time? what should happen at runtime?

Karol Wójcik11:10:14

Wait @U47G49KHQ. Supposing I'll change it to defn.

(defn resolve*
  [module fasym & args]
  (println fasym)
  (println (quote fasym))
  (when-not core.util/nodejs?
    (loader/load module
                      (fn []
                        (if (> (count args) 0)
                          (apply (resolve (quote fasym)) args)
                          ((resolve (quote fasym))))))))
Still I have to quote fasym so that resolve will get the quoted symbol.

vlaaad11:10:32

fasym will be qualified symbol since you create qualified symbol when calling this fn

vlaaad11:10:39

no need to quote it

vlaaad11:10:15

just use (apply resolve fasym args)

Karol Wójcik11:10:18

Nono. Resolve will return the function, then I can apply the args. I need the quoted, fully qualified symbol for cljs.core/resolve. Otherwise I got:

src/core/lazy.cljs Assert failed: Argument to resolve must be a quoted symbol
                            (core/and (seq? quoted-sym) (= (quote quote) (first quoted-sym)))

Karol Wójcik11:10:24

Indeed if I use the defn I will have: view.core/importer symbol bound to fasym. But still I need to quote it for resolve.

vlaaad11:10:19

oh, sorry, on cljs require is a macro, I was thinking about clj

vlaaad11:10:42

I meant resolve..

vlaaad11:10:37

it's strange you are able to even call it with apply, since there you need function value, and you can't take value of a macro

Karol Wójcik11:10:59

Well. Probably I would not be able to use apply. For now i don't know, because I cannot produce the quoted qualified symbol.

Karol Wójcik11:10:01

How can I quote fasym in function definition in the way that It will be 'view.core/importer rather than 'fasym?

vlaaad11:10:50

you can try just creating a list with a 'quote symbol: (list 'quote fasym)

Karol Wójcik11:10:15

And then eval it.

vlaaad11:10:14

I meant just pass it to resolve if you can get value's macro as a value for some reason 😄

vlaaad11:10:39

(apply resolve (list 'quote fasym) args)

Karol Wójcik11:10:14

Resolve macro expects fully qualified, quoted symbol.

Karol Wójcik11:10:24

Now It will receive a list. Therefore compilation fails 😞 @U47G49KHQ

vlaaad11:10:05

that means that you should have fully-qualified symbol during compile, so you can't pass (symbol "view.core" (name asym)) to your macro, it has to be view.core/inspector

Karol Wójcik11:10:03

Ok thanks! So the helper is useless, because it won't help

lilactown16:10:33

also in general require is only there for helping with a REPL

lilactown16:10:15

not sure what you’re trying to do, but if you want to deploy a CLJS app using require it probably won’t work

JanisOlex12:10:13

question... if clojure function is given enum class, how can clojure test against it? I mean... (defn whoamI [x] (somehow_test_that_x_is_enum x))

vlaaad12:10:04

(instance? Enum x)

vlaaad12:10:47

oh, x is class and you want to check if that class is enum?

vlaaad12:10:24

in that case (.isEnum ^Class x)

lilactown17:10:51

I frequently find myself writing functions and macros lately that really would benefit from being able to know what name it’s being bound to

hiredman17:10:41

that is bad, get a rubber band and put in on your wrist and snap it everytime you find yourself doing that

😆 12
sogaiu22:10:16

that's brilliant -- i just got myself a rubber band 🙂

lilactown17:10:59

it’s usually for devtooling

lilactown17:10:55

for example, when generating CSS classes:

(def my-styles (styles/css {:color "red"})) ;; would like to add the namespace/var-name to the classname generated by this

(defn my-component []
  (let [isolated-styles (styles/css {:color "blue"})] ;; would like to add the namespace and function it was generated in
    ...))

lilactown17:10:34

there are many other examples I’ve actually ran across lately. mainly building out abstractions to help build applications

lilactown17:10:09

what I find is that this leads to proliferation of def<thing> macros which I distaste

hiredman17:10:19

the problem with using things like the namespace and def'ed name as names in things like css is it makes it annoying to call a given function more then once and compose the css you get from it, since the names can collide

lilactown17:10:10

there’s already mechanisms to ensure that doesn’t happen

lilactown17:10:40

but when I’m staring at a classname that looks like css-1def489, it’s a bit harder to figure out where that class comes from

lilactown17:10:21

whereas css-1def489-my-app_components_base-button would be much nicer

lilactown17:10:05

this is just an example, too. there’s other things that you want to parameterize but still be able to relate to the name it was defined

lilactown17:10:33

e.g. re-frame does this itself, but using keywords, which means that jump-to-definition and whatnot doesn’t work

lilactown17:10:09

it needs the name as data so that you can use the 10x devtools and log it etc.

thheller17:10:16

Cursive has find-usages for keywords which works quite well with namespaced keywords

thheller17:10:57

I would recommend just taking an additional/optional keyword for naming purposes?

thheller17:10:09

(styles/css {:color "blue"} ::whatever)?

lilactown17:10:10

yes, it works OK. it just means that you have to add your own var system essentially (what re-frame does) and you can’t treat the thing as a first-class value

lilactown17:10:19

or you end up with repetition

hiredman17:10:35

(which is the same thing I said)

lilactown17:10:47

(def my-styles (styles/css {:color “blue”} `my-styles)) would work

thheller17:10:05

what is the problem with macros though?

lilactown17:10:56

proliferation of defcss, defsub, deffx, etc. gets tedious and also as someone new to a framework, scary

lilactown17:10:11

what are all these def<thing> macros doing? what secrets do they hold? 😛

thheller17:10:05

well they shouldn't hold any secrets. the macro should litereally just (def my-styles (styles/css {:color “blue”} ::my-styles)) but out of (defcss my-styles {...})

thheller17:10:32

so the user can either use the macro or the "convenient" form

lilactown17:10:12

it doesn’t solve the let problem also

thheller17:10:43

yeah I know ... it sucks. the JS world does it completely without names too though. maybe not actually a problem worth solving in the end

lilactown17:10:13

well there are babel plugins that do this. but babel allows slightly more power w.r.t. whole-program processing I believe

bfabry18:10:42

could write a macro record that knows about the various different types of “things” you create in your codebase and records a mapping of thing-identifier->source_file:line_no

emccue18:10:58

ive written a def macro that made a record type and specializations for some multimethods and protocols

emccue18:10:10

it was evil but i stand by it

thheller22:10:01

quick question: is anyone here using REBL? the cognitect REPL UI thing? if so: do you use tap> at all?

dpsutton22:10:36

i use tap> indepdently of rebl

thheller22:10:10

how to you tap? just the value or do you "decorate" it in any way? like add some other context?

thheller22:10:27

(tap> [:some-identifier the-actual-val]) or so?

thheller22:10:59

tap> kinda feels like it is a bit too generic

dpsutton22:10:14

i've just dumped into a rotating list of the last ten forms, i've made a spy tap macro that puts the form its tapping, i've made some that return tuple style like you said

dpsutton22:10:19

not too standardized yet

dpsutton22:10:51

which ever one is nearest at hand or already written is what i use. probably wouldn't care too much between them

thheller22:10:23

yeah I'm building a REBL inspired UI (with CLJS support) and never used tap> before

lilactown22:10:27

@thheller I’ve definitely gotten in the habit of (tap> [:something-to-searhc-for the-actual-val])

thheller22:10:49

started a bit but just (tap> raw-value) just is missing too much context in the UI

lilactown22:10:06

the tool I built for REBLy CLJS support primarily uses tap> right now to communicate

lilactown22:10:59

I think I want to eventually have it export a macro that provides the form being sent just like REBLs inspect

thheller22:10:29

the good thing about tap> is that you don't need a :require for it

thheller22:10:44

kinda want to avoid a custom ns for it

thheller22:10:43

but yeah some structure to "label" values would be nice for the UI. maybe even something that controls where it goes or is handled {:stream :something :keep-latest true} (to only keep the latest values)

thheller22:10:28

maybe just some convention on the actual value, like using a map with some namespaced identifier keys

thheller22:10:42

dunno, just curious to hear if and how people use it overall

lilactown22:10:52

could also use metadata

thheller22:10:39

not all values support metadata though, but yeah that was the other idea

thheller22:10:07

(tap> ^:meta some-val) does nothing too

lilactown22:10:15

your tap handler can get the meta out of the val and pass it along to your REBL app

thheller22:10:55

no. the above doesn't work since it applies meta to the some-val symbol, not the actual value

thheller22:10:54

so it would require a with-meta which makes it inconvenient again

seancorfield23:10:33

(tap> ^:foo [42]) -> the metadata is lost in the way REBL renders tap output so when you browse tap output it ends up just being [42]

seancorfield23:10:52

(and, yeah, I use REBL all-day, every-day so I'm happy to answer Qs about it)

seancorfield23:10:30

I tend to use (tap> [:tag val]) or (tap> {:tag val}) if I want to track where the value came from.

👍 4
lilactown23:10:34

we were talking about the case of ^:foo bar which does not attach meta to the value

thheller23:10:21

@U04V70XH6 do you know how REBL handles the tap history? does it just store the last X entries or something more sophisticated?

thheller23:10:45

my objective right now is to build a UI that can display the shadow-cljs build state remotely

thheller23:10:56

regular print is several hundred mb so that is not an option

thheller23:10:09

but yeah if I just tap too many of those I run out of memory 😛

seancorfield23:10:16

I'm not sure what REBL's limit is on taps. I've run tests with thousands of tap> calls and REBL seems to just store them all until you "clear" the list.

lilactown23:10:37

Could delay deserialization until user selects the specific tapped val

lilactown23:10:21

But I think also there is a semantic difference between viewing a tap to value in the current state of process or resource

thheller23:10:41

serialization is not the issue, that is done on demand

thheller23:10:09

just holding onto too many values is a problem

lilactown23:10:24

Between viewing a tapped value and the current state of process or resource**

thheller23:10:32

but I'm basically designed this with using the most extreme case as a test case

lilactown23:10:38

That's pretty crazy lol

thheller23:10:02

if I can "browse" a value thats several hundred MB in total then the rest is easy 😉

lilactown23:10:37

Maybe you can compress the values over a certain size in memory

thheller23:10:49

you are misunderstanding the issue

thheller23:10:25

tap> values are held into locally. basically just (swap! some-atom conj tap-val)

thheller23:10:54

and external process will then "query" the some-atom

thheller23:10:16

the issue is that some-atom can get too large if I don't add some kind of GC to it

thheller23:10:32

I crashed with OOM a couple of times in my tests already

lilactown23:10:55

So it's not on the GUI side but the app process

thheller23:10:17

yes. I call it the "runtime" which currently is either CLJ or CLJS

thheller23:10:34

but the original value is always in the runtime and the tool can request "fragments" of it

thheller23:10:47

like give me the first 5 entries of a map or seq

lilactown23:10:57

That's very similar to the way my attempt at this works

thheller23:10:59

then give me the next 5 etc

lilactown23:10:18

I haven't worked with such large values

lilactown23:10:49

Biggest value is the app-db of our 40k line re-frame app, which isn't in the magnitude of 100s of mb

thheller23:10:02

hehe yeah it is kind of unusual. but I'm building it to solve my problem in shadow-cljs in that I can never really look at the actual build-state at any point

thheller23:10:07

since printing it is too large 😛

thheller23:10:51

REBL was the only option that worked and it felt like a super power

thheller23:10:05

but its in process and doesn't support CLJS ... so I need something else 😉

lilactown23:10:29

yeah I am very curious now if REBL can handle that, how it’s managing to

thheller23:10:36

well .. gotta sleep. I hope I have something usable soon that I can share. 🙂

thheller23:10:54

REBL is running in process. it never has to serialize anything.

lilactown23:10:27

but it still needs to access old tapped values if you select and nav into them yeah?

lilactown23:10:34

so they have to be in memory somewhere right? :thinking_face:

thheller23:10:51

yes, but that is where they always are

thheller23:10:09

I would guess it is just putting everything into an atom somewhere

lilactown23:10:22

isn’t that what we’re doing?

lilactown23:10:40

don’t let me keep you from sleep… we can talk about it another time

thheller23:10:41

in the runtime yes

thheller23:10:05

but when the "tool" or UI is running somewhere else things become complicated

dpsutton22:10:23

i wish there was a notion of topics built in

lilactown22:10:04

I was thinking of something like (inspect> (foo bar) #{:topicA :topicB})

dpsutton22:10:49

Unfortunately then you need a namespace providing that

thheller22:10:23

well we can always sneak a var into clojure.core 😉

dpsutton22:10:55

Ha. I was just thinking about that and then making sure it’s not there on prod builds and a nice warning when found

sogaiu22:10:59

@thheller with tap> in rebl, you accumulate multiple things before clicking the browse button -- so you can tap> a keyword to use as a fake label, no?

dpsutton22:10:06

Ohh, could you datafy the value? So it’s a bunch of keyword “topics” and you get the value by nav’ing to them

lilactown22:10:49

doesn’t seem like a good use of datafy/nav

sogaiu22:10:53

would a tap-fn that destructures [:label value] and then calls submit with the pieces work?

thheller23:10:56

well the problem is identifying if something was actually meant as a label

thheller23:10:17

but I guess if everyone followed that convention it would be fine

sogaiu23:10:26

perhaps it would be nicer if rebl's ui provided a text field for labeling what was tapped in the tap pane

seancorfield23:10:08

@sogaiu Not sure how that helps if you have 1,000 tapped values?

sogaiu23:10:03

don't know that i would use that for a 1000 tapped values, but once i tap something, to get it to appear somewhere useful i click on the browse button -- but typically the value shows up in the browse pane with no label

sogaiu23:10:34

isn't it a bit inconvenient in telling things apart after a few?

seancorfield23:10:52

I clear the taps before a specific test run. Run the "test". Browse the taps. That shows up as :rebl/taps with an array of all the tapped values as its corresponding value, so you navigate (->) into that and then scroll up and down (^ / v).

sogaiu23:10:36

yes, that's for a single run

sogaiu23:10:50

i have several sets that i compare

seancorfield23:10:00

So you're only looking at the tapped values for a single test run that way. If you need to identify the tap> locations, use [:tag val] or {:tag val} as the value you tap> so the tag is unique.

seancorfield23:10:23

Comparing across multiple sets of :rebl/taps?

sogaiu23:10:26

the ui has a spot for a name that gets used for submit right?

seancorfield23:10:42

You mean the def as...?

sogaiu23:10:19

when you use cognitect.rebl/submit

sogaiu23:10:22

it has two arguments

sogaiu23:10:32

one of them shows up as a sort of label for the value

seancorfield23:10:39

The code and the value.

sogaiu23:10:47

you don't have to make it code

sogaiu23:10:52

you can use whatever string 🙂

seancorfield23:10:59

True, you could...

sogaiu23:10:07

that's what i've been doing 🙂

sogaiu23:10:19

helps to distinguish things

seancorfield23:10:19

So you'd want a way to swap out the generic :rebl/taps for a specific name you provide?