Fork me on GitHub
#clojurescript
<
2017-04-02
>
lsenta07:04:44

What's the correct way to define a macro in a cljc to be used from cljs?

lsenta07:04:40

I have something like

(ns current
  #?(:cljs 
        ;; import itself as a macro in cljs
         (:require-macros 
            [current :refer [my-macro]]))

#?(:clj
   (defmacro my-macro
     [db-sym & body]
     `(let [~db-sym 2]
        (current/some-fct
            (superv.async/go-try S
                                 ~@body))))))

lsenta08:04:42

But I keep getting warnings use of undeclared var current/some-fct in the places where I use the macros

darwin08:04:26

@lsenta here is how I would do it, the macro is written in a clj file, shared between both clj and cljs and decides what code to generate based on cljs-env? predicate, also uses CLJS-1507 to reduce reader conditionals noise: [1] https://github.com/binaryage/env-config/blob/master/src/lib/env_config/impl/coercers.cljc#L4 [2] https://github.com/binaryage/env-config/blob/master/src/lib/env_config/impl/macros.cljs#L6 [3] https://github.com/binaryage/env-config/blob/master/src/lib/env_config/impl/macros.clj#L3

lsenta10:04:40

@U08E3BBST thanks for the advice, I'll try this approach next time. I think I need to work my way around "targeting directly the compiler phase". How would you deal with the imports in the macros.clj?

(if (cljs-env?) 
   (ns my.ns
     (:require [cljs.core.async ...]))
   (ns my.ns
     (:require [clojure.core.async])))

thheller09:04:16

@lsenta is the current actually a namespace with one segment? ie. no .?

lsenta09:04:07

@thheller it's just for the shortness of the example. Could that change something?

thheller09:04:29

the CLJ side doesn't know the aliases you define in CLJS

thheller09:04:43

so you need to use the full namespaces

thheller09:04:16

which you do for superv.async but maybe not for current?

lsenta09:04:39

you lost me here, I have a (ns something) declaration at the top of my file, what's an alias? a (def alias 'my.long.namespace)?

thheller09:04:07

(ns something (:require [something.else :as alias]))

thheller09:04:23

and then in the macro (alias/foo)

thheller09:04:30

instead of something.else/foo

lsenta09:04:43

Ho go it, in that case it's a single cljc that contains all the code

thheller09:04:21

then use (some-fct ...) (in the macro)

thheller09:04:52

hmm although ... I'm confused by the current .. single segment namespaces are usually treated as aliases

thheller09:04:16

so that might be the problem instead if it actually is

lsenta09:04:32

Nope, it's a regular one a.b.c πŸ™‚

lsenta09:04:59

@thheller without the namespaces in the macro I get the same warning

lsenta09:04:36

I've tried adding them to mimic the go-try example

thheller09:04:39

hmm if you use the full namespace you should be fine

lsenta09:04:07

Ok now I got it, the warning disappears when I import the namespace

lsenta09:04:12

When the macro my.ns/my-macro is expanded, the clj compiler writes the symbol (my.ns/some-fct ...)

lsenta09:04:39

but then the cljs compiler doesn't know the symbol, I have to add a regular (:require [my.ns]) for it to work.

lsenta10:04:17

I guess I got it, is there anyway to deal with that? that's so leaky

thheller10:04:01

ah the macro is from another namespace? no they must always be required

lsenta10:04:36

I work in namespace a.a. my macro M and the functions f are in namespace a.b In namespace a.a, in cljs, if I require the macro M and use it, it'll tell me the function are missing

lsenta10:04:21

If I both require-macro M and the whole namespace a.b then it works

lsenta10:04:38

which is messed up, if macro depends on n namespaces then I need to import them all in every client namespaes?!

thheller10:04:16

just add (ns a.a (:require-macros [a.a ...]) (:require [a.b]))

thheller10:04:43

if you know that the macro defined in a.a will emit something from a.b

lsenta10:04:25

the macro is self contained in the a.b namespace

lsenta10:04:40

a.a is only using the macro like (M 42)

lsenta10:04:45

Here's a dummy example:

lsenta10:04:30

(ns a.b ;; cljc
  #?(:cljs (:require-macro [a.b :refer [M]])))

(defn f [x] 
  ...)

(defmacro M [k & body]
   `(let [~k 12]
       (f 12)
       ~@body)))
 

lsenta10:04:20

(ns a.a ;; cljs
   (:require-macro [a.b :refer [M]]))

(M hello "leaky")

lsenta10:04:42

Warning, a.b/f not found

lsenta10:04:56

(ns a.a ;; cljs
   (:require [a.b])
   (:require-macro [a.b :refer [M]]))

(M hello "leaky")

thheller10:04:59

(ns a.a (:require [a.b :refer [M]]))

lsenta10:04:48

:require, :refer a macro?

thheller10:04:17

yes, if the namespace that provides a macro has a :require-macro to itself that works

thheller10:04:44

otherwise :require-macros is leaky in that way yes, since that just requires the clojure file

lsenta10:04:14

Haha and I thought I was starting to understand the macro expansion phase, the fact that just require works is mindblowing

lsenta10:04:07

thanks for the help @thheller o7

thheller10:04:53

np πŸ™‚

thheller10:04:26

FWIW a lot of problems go away if you only use :require-macros in a namespace to require itself

thheller10:04:37

and then stick to :require/`:refer` everywhere else

sb10:04:37

Hi Guys, I would like to use `lein-cljsbuild β€œ1.1.5”’ and newest clojurescript dependency. I got bug, but with clojurescript 1.7.170 and lein-cljsbuild 1.1.1 ok. What is the best option? (sorry for the beginner question, maybe do you have more experience and quick answer)

thheller10:04:54

@sb whats the error you are seeing with 1.1.5?

thheller10:04:49

that looks like you are using a very new clojurescript release but somehow get an old clojure-compiler

thheller10:04:10

what does your lein deps :tree look like? does it complain about conflicts?

sb10:04:23

Thanks probably the lein unit overwrite the org.clojure/clojure. One moment. I test it.

thheller10:04:26

that looks fine

sb10:04:19

Yes, but I don’t know what is the problem in this case.

sb10:04:29

I use the older versions.

thheller10:04:51

when in doubt: did you run lein clean after upgrading the version?

sb10:04:56

I test it again. Yes. I tried several times, but maybe..

sb10:04:12

I reset my test server and we will see, if that is cache problem, ..

sb10:04:31

Ok, similar error message. I use now the old versions, later I will find the problem.

sb10:04:51

@thheller thanks the help!

thheller10:04:42

hmm might be something in lein-cljsbuild .. the clojurescript 1.9.495 is not an official release yet

sb10:04:57

Yes, therefore maybe..

yubrshen11:04:51

I have to develop in C++, but I wonder if there is any possibility to use Clojure to implement some of form of test for C++ code, treating the C++ code as data? I feel hopeful of the possibility after Watching the talk of Magic: Boom. Symbolic Assembly: Using Clojure to Meta-program Bytecode by @ra https://t.co/wzMBdKEvn6 Any pointer or comment is appreciated. I hope that the approach would increase productivity and rigor othe tests.

dottedmag15:04:58

How do I run macroexpand in cljs REPL for my macros? In Figwheel REPL I can evaluate (macroexpand '(int 5)), but (macroexpand '(my-macro)) returns the from unchanged.

mfikes23:04:13

@dottedmag Be sure to define your macros in a Clojure source file and use require-macros on its namespace name in the REPL.