Fork me on GitHub

is js-obj somehow not there anymore?


new clojurescript gives me lots of undeclared var gobject/set


@leongrapenthin just a regression, fixed in maser


will likely cut a release sooner rather than later just for that

Kevin Lynagh03:08:57

Is there a way to retrieve the cljs source of a function at runtime? It's for a development build, so happy to do some weird hacks.

Kevin Lynagh04:08:25

I'd like to get the source as a string so I can send it over the wire.

Kevin Lynagh04:08:37

conceptually, something like: (-> (fn [x] :foo) meta :source) ;;=> "(fn [x] :foo)"

Kevin Lynagh04:08:40

Not trying to capture the actual execution context, closures, or otherwise re-eval on the other side of the wire. It's purely for instrumentation / debugging.

Kevin Lynagh05:08:05

@anmonteiro Thanks! It looks like that goes through vars, and I need anon fns.

Kevin Lynagh05:08:56

Maybe I'll have to write my own macro to do this, using reader or some such to identify the fn forms and capture the source?


pretty sure there’s no way currently that supports that


so short answer is you have to write your own macro


even then seems challenging


may I ask what’s the use case you have in mind?

Kevin Lynagh05:08:16

I'm developing a debugger for complex UI interactions

Kevin Lynagh05:08:34

the debugger needs to run on a separate machine (e.g., an iPad) so that you can examine certain interactions within the app.

Kevin Lynagh05:08:02

That is, you can't switch over to devtools because the interaction might do things on focus change, keyup, etc.


something like

(defmacro fn+ [& stuff] `(with-meta ~(cons 'fn ~stuff) {:source ~(str stuff)}))


I think he wants to keep using fn

Kevin Lynagh05:08:09

and I'd like to view the fn source on that debugger


I think that's the only choice though


the only general one at least


wouldn’t work for third-party libraries

Kevin Lynagh05:08:08

I could work with that

Kevin Lynagh05:08:30

is it possible to replace a var in cljs compilation settings somehow?

Kevin Lynagh05:08:46

I'm building via

Kevin Lynagh05:08:11

presumably I could remap "fn" in the development mode compilation to add source metadata


+user=> (defmacro fn+ [& stuff] `(with-meta ~(cons 'fn stuff) {:source ~(str stuff)}))
+user=> (def f (fn+ testing-it [_] 42))
+user=> (pprint (meta f))
{:source "(testing-it [_] 42)"}


@kevin.lynagh pretty hacky, but I think you can


if you’re using you can override the fn macro 🙂

Kevin Lynagh05:08:21

@noisesmith that looks perfect, thanks!


but I think this needs to be before you even load any cljs namespaces

Kevin Lynagh05:08:04

@anmonteiro Would it be possible to do on a per-namespace basis? Overriding every fn seems like it could blow up the code size significantly.


@kevin.lynagh well you can override it and dispatch on some metadata marker that records the source


(fn ^:record-source foo [] ...


also you could emit the sources to some source map type file, and just keep an id for lookup on the fn


that might even make tooling easier actually

Kevin Lynagh05:08:03

Here's another idea --- given that I'm invoking build myself, would it be possible to just dynamically replace the inputs to cljs without having to do fancy alter-var-root stuff?

Kevin Lynagh05:08:25

@noisesmith I'd like to avoid trying to send source maps over the wire on this


what do you mean by replacing the inputs?

Kevin Lynagh05:08:44

(cljs/inputs "src/")


what I’m saying is CLJS imports the fn macro from Clojure


you can just make a copy of that


(in-ns 'clojure.core)

(defmacro fn ...)

(in-ns 'user)

Kevin Lynagh05:08:11

bashing vars like that makes me nervous

Kevin Lynagh05:08:19

that it'll end up sneaking into the production build somehow


also think about whathappens if that file gets loaded twice - it ends up wrapping the wrapper if you are not careful

Kevin Lynagh05:08:21

by replacing the inputs, I mean replacing the symbols before feeding into cljs compiler. So I could replace (fn ... with (fn-with-source ...


right but for that to really work you would want to transform things like defn that expand to fn


another alternative


and that's what macros are good at


is overriding the analyzer method that analyzes fn*


and you can do it conditionally of compiler options so you make sure it doesn’t bloat your prod build


but that’s going to take some work

Kevin Lynagh05:08:05

I'm not very familiar w/ the internal workings of the analyzer --- is this something in cljs, or is this tools.analyzer?



Kevin Lynagh05:08:05

this would be overriding a multimethod?

Kevin Lynagh05:08:09

That's about the same comfort level zone for me as var-bashing.


I didn’t tell you it was going to be easy


I’m just enumerating alternatives


couldn't a user opt in by telling the tool to reload an ns with its version of fn?

Kevin Lynagh05:08:51

@anmonteiro Reading that code, it seems like that all operates on code that has already been read and macroexpanded?

Kevin Lynagh05:08:09

It's not obvious to me how to get a handle on the original source


form is the source


[_ & args :as form]

Kevin Lynagh05:08:24

by "source" I mean the original string

Kevin Lynagh05:08:07

isn't form a seq of symbols and stuff?


yeah, now you are probably talking about replacing the reader...

Kevin Lynagh05:08:10

@noisesmith Right. The thing that looks at the var actually just looks back on disk to find the source:

Kevin Lynagh05:08:46

Well, in any case running str on the forms will be sufficient for my purposes. I think var-overriding is my best bet, so I'll look into that.


@kevin.lynagh are you aware that Chrome DevTools can be used remotely from a different machine? That should be enough to debug focus/keyboard/hover issues.

Kevin Lynagh17:08:57

Yeah, I've used remote devtools for debugging before. I'm working on something that's more specialized than the built-in devtools

Kevin Lynagh17:08:14

Thanks for cljs-devtools, by the way!


what editor do you use on linux?


if we are taking a poll, vim


not a poll, i am taking suggestions


i had spacemacs, but that can't even comment the code by default and when i googled it i got lost


Can anyone explain to me how it’s possible to use, say, clojure.string inside Clojurescript/Lumo where no JVM is involved? I don’t get it.


I think there's a list of Clojure requires that get rewritten to CLJS requires in ClojureScript


@clojer It is the same reason you can use it in code emitted by JVM ClojureScript: That namespace is implemented in ClojureScript which compiles to JavaScript essentially implementing all the needed functionality.


@rgdelato You are referring to the fact that clojure.* gets aliased to cljs.* if the clojure.* namespace doesn't exist.


oh, cool. I didn't know it was across the board, I thought it was only specific libs


the pattern is probably "does this ns need to define macros?"


clojure.string isn't a namespace that defines macros, so it doesn't need a rename


@noisesmith yeah. Oftentimes a rename is done to avoid a collision of the ClojureScript macros namespace with the existing Clojure namespace


if you read Chinese, help me with this book:


I have a line of cljs code like this:

(.addEventListener js/window "keydown" (fn [e] (println e)))
It works as expected when run in the browser, expect, when a key is pressed, it outputs this: #object[KeyboardEvent [object KeyboardEvent]]


Is there a way to get the js object printed to the console rather than the above?

Kevin Lynagh18:08:51

@tkjone Yep! In this case, println is trying to print out a cljs value, but it was given the native JavaScript event.

Kevin Lynagh18:08:15

You can still use console.log(e) from JavaScript --- in ClojureScript that would look like (.log js/console e)


Thanks, @kevin.lynagh! So for instances like what I am trying to do, this is the preferred practice?

Kevin Lynagh18:08:11

In all of my projects, I define two macros for printing:

Kevin Lynagh18:08:35

The p can be used for things that I know are native JavaScript objects, and the pp is used for cljs objects. The reason I'm using a macro is so that I get proper line numbers in the JavaScript console.

Kevin Lynagh18:08:04

If you were to define a function that called console.log, then all of the line numbers would refer to that function definition rather than to the place you were actually trying to print.

Kevin Lynagh18:08:44

Also note that, unlike console.log (in js) and pr-str (in Clojure/ClojureScript) these macros return the value, which means you can just throw it wherever you want in an existing expression

Kevin Lynagh18:08:49

Maybe that was more info than you wanted --- in short, yes: it is totally idiomatic to use the platform stuff like console.log to print things = )


haha that was excellent, Kevin.


I was just going to ask why a macro over a function, but you beat me to it!

Kevin Lynagh18:08:13

I very rarely define macros. I have a 10k+ line clojurescript project and those are the only two macros. Only reason is to get 'dem line numbers.


yes, macros seem like something to add under careful consideration, but this sounds right


I also notice that it continues to print out the cljs value of #object[KeyboardEvent [object KeyboardEvent]] - this is expected behaviour, yes?

Kevin Lynagh18:08:20

If you are working in a REPL then yes, your original event listener is still attached to js/window.

Kevin Lynagh18:08:22

Figwheel would also count as a REPL in this case, since the stateful window object is persisting between refreshes.


don't want to probe too much, but I am curious where you put those macros in your project?...from a files and folders perspective

Kevin Lynagh18:08:57

@tkjone Something like: src/foo/bar/macros.clj

Kevin Lynagh18:08:41

Then in my cljs files: `(ns (:require-macros [ :refer [p pp]]))`

Kevin Lynagh18:08:15

uh, I don't know how to slack. but you get the idea.


yep, I see what is happening.


I cloned a repo to a VPS and after I lein deps'd everying, my source takes minutes building, not even from scratch. Is there something I forgot to update?


(the repo has been dormant for about six months)


@kevin.lynagh I added your macros directly in the same cljs file as my main app. This actually caused, without warning or errors, the script to stop working. I moved them out of the cljs file, moved them into their own clj file and imported them into my cljs - as you noted to do - and everything works again. Is this because the macros are, CLJ and not CLJS?


or maybe I just did something weird in general


@tkjone Macros must be defined in either *.clj files or *.cljc files.


(The true answer is a bit more complicated as macros can be written using ClojureScript source, for self-hosting.)


and its expected that doing something like like this will not throw warnings of any kind, correct?


If you define a macro in a *.cljs file it will essentially generate the macro function and if you try to call it you will find that it won't expand properly. I don't believe that there are currently any compiler diagnostics that get emitted if you do this, but there probably could be. At its root is, that for self-hosted ClojureScript you can define a macro in a file being compiled as ClojureScript.


I'm curious, for devs familiar with other transpile-to-JS languages (Elm, Scala.js, ReasonML): Do any of them sit atop Closure, or do they employ other mechanisms to achieve the same things ClojureScript does (namespace / module system, advanced minification)?


I think most either don’t have the notion of namespacing, or they emulate them with CommonJS modules


Scala.js uses Closure for optimization, not sure if they use Closure lib or modules


I’m running a ClojureScript script on AWS Lambda and my environment variables aren’t coming through; cljs.nodejs.process.env is just {:NODE_ENV “production”}. This seems to be set in the source: var process={env:{}};process.env.NODE_ENV="production"; — although I’m not sure where that’s coming from. Ring any bells to anyone?


@lvh set :process-shim false in your compiler options


okiedokie 🙂


is there a ticket I should reference because that seems like a little mysterious


haha nice, thanks 🙂


this is the relevant commit


it’s a new feature and we’re setting the wrong default under Node.js


cool well at least it’s a useful issue to report


debugging this was a little crazy because the observed behavior is that AWS Lambda env vars just… don’t work


just attached a patch to that issue


IMO this is critical so I think it will be looked at soon and included in the next release


workaround works fine btw 🙂


Does format exist in clojurescript?


(format "audio[data-key=\"%s\"]" key-code)


@tkjone in Lumo:

cljs.user=> (require '[goog.string :as gstr])
cljs.user=> (require 'goog.string.format)
cljs.user=> (gstr/format "%s" "foo")


a ClojureScript REPL


you can use the same functions in your ClojureScript namespace:

(ns my.ns
  (:require [goog.string :as gstr]


Not that I am worried about performance at the moment, but do you know if this goog.string.format would have similar performance considerations as clojures format method. My knowledge of this is based on this article:


dunno about perf


Any known downside, aside from readability?


not that I’m aware


@tkjone here’s an article that demystifies goog namespace requiring


What is a good way to go about checking if a value is null in clojurescript?


@tkjone (.-isNull js/goog) can be used


I suspect if you wanted portable code, this may be sufficient: (fn [x] (identical? nil x))


(Code that is portable to Clojure.)