This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-07-30
Channels
- # babashka (44)
- # beginners (29)
- # calva (80)
- # cider (11)
- # clara (1)
- # clj-kondo (9)
- # clojure (80)
- # clojure-europe (21)
- # clojure-france (13)
- # clojure-nl (4)
- # clojure-spec (3)
- # clojure-uk (6)
- # clojurescript (72)
- # code-reviews (43)
- # cursive (11)
- # datomic (27)
- # events (13)
- # figwheel-main (12)
- # fulcro (27)
- # graalvm (1)
- # jackdaw (2)
- # kaocha (1)
- # malli (4)
- # meander (13)
- # nrepl (2)
- # pathom (8)
- # re-frame (4)
- # reagent (7)
- # reitit (9)
- # remote-jobs (1)
- # reveal (56)
- # ring-swagger (2)
- # sci (5)
- # shadow-cljs (20)
- # slack-help (2)
- # tools-deps (96)
- # vim (7)
- # xtdb (32)
I need help with macros.
I’d like to create a macro defnmy
similar to `defn` but that creates a function whose name is transformed by a function supplied to the macro.
Example of usage:
(defnmy FOO clojure.string/lower-case [x]
(inc x))
It should create a function named `foo`
I was able to implement it using `resolve`:
(defmacro defnmy [name fn args body]
(let [n (symbol ((resolve fn) name))]
`(defn ~n ~args ~body)))
I would like the macro to workd on self-hosted cljs.
The problem is that in cljs resolve
is a macro and it works only on non-namespaced symbols.
Help appreciated> resolve is a macro and it only works on non-namespaced symbols
(ins)user=> (resolve ')
#'
(ins)user=> (map resolve '[+ - inc dec])
(#'clojure.core/+ #'clojure.core/- #'clojure.core/inc #'clojure.core/dec)
it is not a macro, and it works on both namespaced and non-namespaced symbolsthe problem is that cljs doesn't have real vars, and resolve only works on vars
I meant that in ClojureScript, resolve
is a macro
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/core.cljc#L3411
The context of my request is that I am trying to make this library https://github.com/clj-commons/camel-snake-kebab self-host compatible and it has this `defconversion` macro https://github.com/clj-commons/camel-snake-kebab/blob/master/src/camel_snake_kebab/internals/macros.clj#L21
Did you find any disadvantage of using https://github.com/binaryage/chromex vs raw JavaScript? This is library for google chrome extension
. I have no experience with this, but I need to write something and trying to decide if do this with cljs or maybe in this case with js.
@viebel I'd do it this way https://gist.github.com/mfikes/c45ce81705786a04e617a419941afcab and, for the self-hosted case copy enough of the Planck code that bottoms out in the use of eval
to pull it off.
An issue might arise if the code referenced in the function itself needs to be loaded; then there would be asynchronous issues to cope with.
@mfikes your gist is awesome! Thanks
But it makes me asking myself what would be the meaning of making a lib like https://github.com/clj-commons/camel-snake-kebab self-host compatible.
One option is to make the lib works fine with planck. In that case, I can use planck.repl/requiring-resolve
But then the lib would not work on other self hosted env (lumo, klipse).
Can you think of another option?
Copy enough of Planck’s implementation of requiring-resolve
into the lib—it is pure ClojureScript that bottoms out on eval
What state would I pass to cljs/eval
as first arg?
Digesting…
Just copy this: https://github.com/planck-repl/planck/blob/2bc618886d6bfa1109bb3d5b5d718a914e855b19/planck-cljs/src/planck/core.cljs#L393-L401
@mfikes Do I need resolve
from planck or I can use resolve
from cljs?
But I can replace cljs.js/eval
by cljs.core/eval
.Right?
No. I meant including code adaptation (not verbatim copy/paste)
(defn ns-resolve
[sym]
(binding [ana/*cljs-warnings* (zipmap (keys ana/*cljs-warnings*) (repeat false))]
(eval `(~'var ~sym))))
If the symbol is fully qualified, it should not matter. Right?
I am missing something in the code of requiring-resolve https://github.com/planck-repl/planck/blob/2bc618886d6bfa1109bb3d5b5d718a914e855b19/planck-cljs/src/planck/core.cljs#L393-L401
eval
runs synchronously or asynchronously?
So what’s the point of requiring-resolve
vs resolve
?
Does planck have a magic trick to run eval
synchronously?
This all surrounds whether the namespace for the function symbol passed to defmy
has been required or not. If not, then resolve
is going to return nil
.
The default implementation of eval
for self-hosted environments is assuming the result of the evaluation is available synchronously. (See https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/js.cljs#L1238)
But if you evaluate a require
form, then it is up to each self-hosted environment to implement that.
In Planck's case require
does indeed have a synchronous implementation.
(Actually it used to be the case that require
needed to be directly implemented by a self-hosted environment, but at some point a macro was introduced, but nevertheless the root issue remains regarding whether cljs.js/*load-fn*
is asynchronous.)
Broadly speaking, you want to write a macro that, upon macroexpansion, makes use of a function that may or may not have been loaded at that time. And in self-hosted ClojureScript, the namespace loading mechanism is fundamentally asynchronous.
If your use case knows a priori that the function symbols being passed to defmy
are already loaded, then resolve
should be sufficient for that use case.
Wow! Thanks for this crystal clear explanation
@viebel If you assume the fn has been loaded maybe you can just dispense with all of the complexity and just use eval
and var
(defmacro defnmy [name fn args & body]
(let [n (symbol ((eval `(~'var ~fn)) (str name)))]
`(defn ~n ~args ~@body)))
I was just thinking about that @mfikes What about setting the cljs-warnings to false like in
(binding [ana/*cljs-warnings* (zipmap (keys ana/*cljs-warnings*) (repeat false))]
(eval `(~'var ~sym)))
I wonder if there are actually any warnings you would want to suppress... the cool thing about avoiding that is that the definition above based on just eval
and var
also works in Clojure (and thus is perfectly fine as a macro for JVM ClojureScript)
Oh nice!
Is eval
and var
preferable over clojure.core/resolve
for Clojure and JVM ClojureScript?
Or at least not worse
That's a good question. You could use a reader conditional and use clojure.core/resolve
if in :clj
, but I guess you are pondering if there is a reason to do that. 🙂
Yeah. That’s my question
is this the world's biggest yakshave to get camel-snake-kebab
working in a self-hosted environment?
is (resolve sym)
(better than (eval
('var sym))` ?
I haven't read all the scrollback, but that library doesn't seem to do something that is fundamentally incompatible with any environment
@ghadi I am not familiar with the yakshave expression. What does it mean?
> Yak shaving refers to a task, that leads you to perform another related task and so on, and so on — all distracting you from your original goal. This is sometimes called “going down the rabbit hole.”
Sometimes the yak shave is so lengthy, a day has gone by, and you have forgotten what you were actually trying to do.
@ghadi take a look at the camel-snake-kebab defconversion macro https://github.com/clj-commons/camel-snake-kebab/blob/master/src/camel_snake_kebab/internals/macros.clj#L21
One aspect that appears to be clouding things is the dynamic ability to pass and resolve arbitrary functions. But the functions that transform function names are limited in scope right?
Ahh, yeah, it feels like this "higher order" macro is nifty, but maybe is also causing the challenges in porting to self-hosted.
When defconversion
is used inside camel-snake-kebab, the scope is indeed limited.
nifty or too nifty (i.e. abuse)?
This is what I thought
My question: is it too clever?
Beign too clever makes sometimes the code hard to decode
@mfikes Thanks to you, camel-snake-kebab works in Klipse now http://app.klipse.tech/?cljs_in=(require%20%27%5Bcamel-snake-kebab.core%20%3Arefer%20%5B-%3Ekebab-case-keyword%5D%5D)%0A%0A(-%3Ekebab-case-keyword%20%3Aaa_bb)&external-libs=%5B%22https%3A%2F%2Fraw.githubusercontent.com%2Fviebel%2Fcamel-snake-kebab%2Fmaster%2Fsrc%2F%22%5D
@ghadi camel-snake-case has 162 lines of code
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
ClojureC 6 31 8 162
-------------------------------------------------------------------------------
SUM: 6 31 8 162
-------------------------------------------------------------------------------