Fork me on GitHub
#clojurescript
<
2017-08-05
>
leongrapenthin00:08:37

is js-obj somehow not there anymore?

leongrapenthin00:08:09

new clojurescript gives me lots of undeclared var gobject/set

dnolen01:08:07

@leongrapenthin just a regression, fixed in maser

dnolen01:08:18

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?

anmonteiro05:08:15

pretty sure there’s no way currently that supports that

anmonteiro05:08:28

so short answer is you have to write your own macro

anmonteiro05:08:48

even then seems challenging

anmonteiro05:08:57

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.

noisesmith05:08:18

something like

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

anmonteiro05:08:47

I think he wants to keep using fn

kevin.lynagh05:08:09

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

noisesmith05:08:11

I think that's the only choice though

noisesmith05:08:22

the only general one at least

anmonteiro05:08:44

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 cljs.build.api/build

kevin.lynagh05:08:11

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

noisesmith05:08:17

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

anmonteiro05:08:47

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

anmonteiro05:08:07

if you’re using cljs.build.api/build you can override the fn macro 🙂

kevin.lynagh05:08:21

@noisesmith that looks perfect, thanks!

anmonteiro05:08:25

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.

anmonteiro05:08:44

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

anmonteiro05:08:56

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

noisesmith05:08:35

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

noisesmith05:08:43

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

anmonteiro05:08:36

what do you mean by replacing the inputs?

kevin.lynagh05:08:44

(cljs/inputs "src/")

anmonteiro05:08:52

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

anmonteiro05:08:02

you can just make a copy of that

anmonteiro05:08:30

(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

noisesmith05:08:50

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 ...

noisesmith05:08:46

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

anmonteiro05:08:49

another alternative

noisesmith05:08:51

and that's what macros are good at

anmonteiro05:08:11

is overriding the analyzer method that analyzes fn*

kevin.lynagh05:08:11

@noisesmith Good point.

anmonteiro05:08:33

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

anmonteiro05:08:42

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?

anmonteiro05:08:21

cljs.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.

anmonteiro05:08:29

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

anmonteiro05:08:35

I’m just enumerating alternatives

noisesmith05:08:51

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

anmonteiro05:08:38

form is the source

anmonteiro05:08:47

[_ & 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?

noisesmith05:08:24

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: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/repl.cljc#L1191

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.lynagh05:08:55

Thanks for the help @anmonteiro and @noisesmith!

darwin08:08:02

@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!

ashnur12:08:13

what editor do you use on linux?

manuel16:08:07

@ashnur Emacs

noisesmith16:08:32

if we are taking a poll, vim

ashnur16:08:46

not a poll, i am taking suggestions

ashnur16:08:05

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

clojer16:08:19

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.

rgdelato17:08:03

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

mfikes17:08:07

@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.

mfikes17:08:56

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

rgdelato17:08:33

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

noisesmith17:08:18

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

noisesmith17:08:31

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

mfikes17:08:30

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

Jon17:08:15

if you read Chinese, help me with this book: https://github.com/clojure-china/cljs-book

athomasoriginal18:08:43

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]]

athomasoriginal18:08:46

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)

athomasoriginal18:08:22

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 = )

athomasoriginal18:08:00

haha that was excellent, Kevin.

athomasoriginal18:08:18

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.

athomasoriginal18:08:00

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

athomasoriginal18:08:22

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.

athomasoriginal18:08:26

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 foo.bar.my-cljs-namespace (:require-macros [foo.bar.macros :refer [p pp]]))`

kevin.lynagh18:08:15

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

athomasoriginal18:08:01

yep, I see what is happening.

captainlexington18:08:38

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?

captainlexington18:08:13

(the repo has been dormant for about six months)

athomasoriginal19:08:27

@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?

athomasoriginal19:08:43

or maybe I just did something weird in general

mfikes19:08:48

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

mfikes19:08:19

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

athomasoriginal19:08:20

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

mfikes19:08:39

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.

mfikes20:08:04

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)?

anmonteiro21:08:41

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

juhoteperi21:08:34

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

lvh22:08:07

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?

anmonteiro22:08:47

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

lvh22:08:04

okiedokie 🙂

lvh22:08:18

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

lvh22:08:25

haha nice, thanks 🙂

anmonteiro22:08:49

this is the relevant commit

anmonteiro22:08:58

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

lvh22:08:35

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

lvh22:08:59

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

anmonteiro22:08:25

just attached a patch to that issue

anmonteiro22:08:49

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

lvh22:08:31

workaround works fine btw 🙂

athomasoriginal22:08:43

Does format exist in clojurescript?

athomasoriginal22:08:11

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

anmonteiro22:08:55

@tkjone in Lumo:

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

anmonteiro22:08:09

a ClojureScript REPL

anmonteiro22:08:47

you can use the same functions in your ClojureScript namespace:

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

athomasoriginal22:08:55

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: http://blog.wjlr.org.uk/2015/01/15/string-interpolation-clojure.html

anmonteiro22:08:16

dunno about perf

athomasoriginal22:08:37

Any known downside, aside from readability?

anmonteiro22:08:34

not that I’m aware

anmonteiro22:08:10

@tkjone here’s an article that demystifies goog namespace requiring https://www.martinklepsch.org/posts/requiring-closure-namespaces.html

athomasoriginal22:08:00

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

mfikes23:08:38

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

mfikes23:08:08

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

mfikes23:08:55

(Code that is portable to Clojure.)