This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-08-31
Channels
- # architecture (1)
- # aws (23)
- # beginners (13)
- # boot (18)
- # cider (5)
- # clara (1)
- # cljs-dev (22)
- # cljsjs (9)
- # cljsrn (28)
- # clojure (120)
- # clojure-canada (12)
- # clojure-dev (6)
- # clojure-italy (4)
- # clojure-korea (1)
- # clojure-russia (18)
- # clojure-sg (8)
- # clojure-spec (45)
- # clojure-uk (12)
- # clojurescript (240)
- # component (4)
- # cursive (17)
- # datomic (91)
- # editors-rus (4)
- # figwheel (2)
- # flambo (6)
- # hoplon (163)
- # instaparse (6)
- # jobs (1)
- # leiningen (2)
- # luminus (5)
- # om (22)
- # om-next (2)
- # onyx (35)
- # perun (15)
- # play-clj (1)
- # protorepl (4)
- # re-frame (106)
- # reagent (4)
- # ring (106)
- # schema (1)
- # spacemacs (17)
- # untangled (40)
- # yada (14)
Reader conditionals still confusing me… this is much better from what I had before:
(:require #?@(:clj
[[clojure.spec :as s]
[clojure.spec.gen :as gen]
[clojure.edn :as edn]
[finops-admin.tables :as tables]
[taoensso.timbre :as log]]
:cljs
[[cljs.spec :as s]
[cljs.spec.impl.gen :as gen]
[cljs.reader :as edn]
[clojure.test.check]
[taoensso.timbre :as log]]))
yet I still don’t know a couple of things.
- What’s with @
sign? Is it really needed?
- How do I define things that used both in clj and cljs. look at timbre for example. Do I really have to list it in both places?@ag: I think this is what most people would do:
(:require
[clojure.spec :as s] ;; <-- auto-aliased to cljs.spec in cljs
[clojure.test.check] ;; <-- ditto
[finops-admin.tables :as tables]
[taoensso.timbre :as log]
#?(:cljs [cljs.reader :as edn]
:clj [clojure.edn :as edn])
#?(:cljs [cljs.spec.impl.gen :as gen]
:clj [clojure.spec.gen :as gen]))
@shaunlebron but I don’t like to have reader conditionals for every single lib, I want to have sections of clj and cljs and “common"
@ag: what you had was fine then
oh, to answer your question about the “common” section, just put that outside the reader conditional 🙂
If I set the :value to something in my state atom in React Native (using re-natal and figwheel), the value flickers between the old value and the new one when I change it. Should I be using a different pattern than to update the state in the on-change property of that component?
Example of properties: https://gist.github.com/domgetter/98c7a53ddb0a3011d1649fdcf23a3e55
There seems to be some contention between the component updating the value to what's being typed, and what's currently in the state atom.
I was trying to implement this example https://reagent-project.github.io/news/news050.html in re-frame But i don't know how to write these reagent/wrap and reagent/cursor functions in re-frame/handlers are these functions are work in re-frame state.
Is there a better way in cljs to check if something is NaN
than using (js/isNan val)
?
@derwolfe anything wrong with js/isNaN
? just write your own helper fn wrapping it, to avoid js-interop in your normal code
nope, nothing at all 🙂 Just want to see if there was a better way that didn't have js
🙂
am I going crazy? do namespace
and name
work differently in cljs? if I’m at the repl, (namespace ‘foo.bar/baz’)
correctly returns “foo.bar”, and (name ‘foo.bar/baz) -> “baz”
… but when the code is running and I step through in the console, both of those functions return symbols?
I personally tend to wrap raw js-interop calls into my own helper functions and shield my normal code from looking like dirty js 🙂 also this allows me not only to use clojure naming conventions, but to encode side-effecting code with !
method names
aha, it looks like upstream an incorrect (symbol ‘foo ‘bar)
wasn’t throwing? perhaps it was getting swallowed somewhere.. both need to be strings if using the 2-arity fn.
hey y'all. What's the convention for when you want to name a variable something in a function body but it conflicts with a builtin global?
@nickt: FWIW cljs.core/clj->js
also works
(defmacro expand [label obj]
(let [expanded (clj->js styles)]
`(def ~label ~expanded)))
and if I do something like (
'clj->js styles)` then the JS output after the cljs->js compile step tries to call core.cljs_-GT_js
or something
@selfsame thanks. That's almost it... the js output I end up with includes a call to cljs.core.clj__GT_js.call
like if I write code (not a macro) that looks like this:
(def my-obj #js
{:first-level #js
{:second-level "hi"}})
then the js output contains my_ns.core.my_obj = {first: {second: "hi"}};
yeah gotcha. I've heard clj->js
isn't super optimized but I use it for most things, other options are instance a (new js/Object)
and set!
the fields or use the js*
macro.
if there's other ways to create js objects in a macro would also be interested in hearing them
@nickt: look into js-obj
, it should be a macro which expands to js object literal if it can
@selfsame there’s js-obj
wow!
fp.user=> (benchmark 10000 (js-obj "foo" 5))
"Elapsed time: 1.820000 msecs"
nil
fp.user=> (benchmark 10000 (clj->js {"foo" 5}))
"Elapsed time: 112.460000 msecs"
how could I recursively walk this cljs map literal and expand it into (js-obj "first-level" (js-obj "second-level" 5))
?
@selfsame that’s because js-obj
has a macro case so it just inlines a literal, but it’s not recursive
heh, yeah my "wow" was realizing I can optimize a bunch of stuff on a project that's not recursive 🙂
dang I'm struggling. Can anybody help me out with a macro that walks a map literal and rewrites it as a (js-obj)
expression?
{:a {:b 1} :c 2}
would become
(js-obj "a" (js-obj "b" 1) "c" 2)
(defn to-js-obj [m]
"Recursively rewrites a map as a (js-obj k v) expression."
(reduce-kv (fn [acc k v]
(if (map? v)
(conj acc (to-js-obj v))
(conj acc (name k) v)) [] m)))
I'm trying to do this as a macro... I feel like I would have to return a quoted js-obj
expression
@nickt spoilers: (defn ->js-obj [form] (cons 'js-obj (mapcat (fn [[k v]] (list (str k) (if (map? v) (->js-obj v) v))) form)))
@jr @selfsame awesome ok yea that's pretty much what I got to: (when trying to expand upon jr's example)
(defn map->js-obj [x]
(if (map? x)
`(js-obj ~@(mapcat (fn [[k v]] [(name k) ~(map->js-obj v)]) x))
x))
Not really a Clojurescript question, but web related... does anyone know of a library for something like Django's Signals framework? Like observables/watchables?
@nickt re: macros in general, https://www.amazon.com/Mastering-Clojure-Macros-Cleaner-Smarter/dp/1941222226 is a good book
you'll need the macro to emit valid cljs code, so you should check the macroexpansion while desigining it
@nickt: if you look closely at my example you’ll notice that the macro expanded to (boot.user/js-obj
@jr I tried to take your line and make it recursive like this:
(defn map->js-obj [x]
(if (map? x)
`(cljs.core/js-obj ~@(mapcat (fn [[k v]] [(name k) ~(map->js-obj v)]) x))
x))
(defmacro defstyles [label styles]
(let [jsform (map->js-obj styles)]
`(def ~label ~jsform)))
yes, that is produced by the implementation of js-obj
, :advanced
mode optimization should simplify it (my guess)
if you want to have full control over emitted js, you could implement simplified version of js-obj macro, using js*
directly
is this what you're talking about? https://github.com/clojure/data.json
@nickt: I’m sorry but you have to do baby steps first, try to read through compiler sources and stdlib (core.cljs), this way you can learn from how simpler things are implemented, for example how js*
“macro" can be (ab)used to emit raw javascript
also you should read that linked book about macros, without it you are just wandering in darkness and wondering about subtle differences between various flavours of quoting/unquoting/splicing in different contexts,
macros is the dark magic
@darwin I understand; I appreciate it. I just came to clojure with an idea in mind that clojure seems well suited to solve, so I'm jumping in on the idea and trying to pick up the relevant pieces of clojure where it suits the application to the idea
(defmacro map->js [x]
(list 'js* "~{}" (json/write-str x)))
(defmacro defstyles [label styles]
(let [jsform (map->js sty)]
`(def ~label ~jsform)))
@nickt IMO your finished macro is totally fine, the emitted js function closure wouldn't make a noticeable difference in perf or gc
@selfsame yea totally. Unfortunately my end-goal is to produce statically-analyzable javascript
you are calling it from within clojure code and it gives you cljs code as data back, it is not intended to expand into another clojure code
it is a "templating language" cljs compiler uses at lower levels, as I said, read the sources
there are some examples, and it is an internal thing, implementation detail, it should not be used in user code unless you are prepared to deal with consequences 🙂
@nickt: you will have to go back to previous js-obj solution, but instead generate raw js*
templates
often things that end in *
are special forms defined in the target's native language
I wouldn't be surprised if js*
was defined in javascript (but I don't know that it is)
being hard-coded in macro-expand it probably acts as a “virtual” macro, it does not exist, but works, I told you, there is magic 🙂 but take my words lightly, this is first time I’m seeing this code: https://github.com/clojure/clojurescript/blob/ddf183681ee56b44913ce027b5795ada193dd344/src/main/clojure/cljs/analyzer.cljc#L2662-L2663
probably the clearest example is looking at the +
macro implementation which uses js*
https://github.com/clojure/clojurescript/blob/r1.9.227/src/main/clojure/cljs/core.cljc#L981-L985
when +
is used where it can be macro expanded, it will just emit a plus sign between the two operand expressions
and it uses js*
for that
@nickt: I’m not sure, analyzer+compiler will probably see JSValue
type thing in place of #js …
#js
is interpreted by the reader, using custom data-readers defined by clojurescript compiler
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/tagged_literals.cljc#L62
Hello, is there a way to find out why result js files are required in order they are required? I have two files (2nd depends on 1st) that are listed inside one :require
(they are required only here), but in cljs_deps.js they are listed in different order.
there are only declarations of deps, not order in which they are brought into the js context
@darwin Yeah, forgot to mention - they depend explicitly. I guess that's the cause of error
JSValue is a deftype: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/tagged_literals.cljc#L60, you should be able to get to the form it is wrapping
(def swag #js {:foo "bar"})
followed by (.val swag)
=> cljs.user.swag.val is not a function
I’m sorry, cannot help anymore from top of my head without writing the code down - have some other work to do 🙂
Let's say I wanted to inspect a namespace and pull out functions with a given metadata value and stick them in a data structure.
1. Can I even do this in ClojureScript? I can't seem to find (ns-map)
at the REPL, at least
2. Would such an operation survive advanced compilation?
@timgilbert yes, you can, collect needed info via a macro and “export” it to cljs by emitting required data
https://github.com/binaryage/cljs-devtools/blob/5b6210920384508e06ba54ae67fe98b2ba849101/src/lib/devtools/formatters/markup.cljs#L437-L452 https://github.com/binaryage/cljs-devtools/blob/5b6210920384508e06ba54ae67fe98b2ba849101/src/lib/devtools/formatters/markup.clj#L10
Oh, interesting, thanks. Never seen &env
before, is it like a cljs internal or something?
@timgilbert here is another example of extracting var info-map via #’
in a macro: https://github.com/binaryage/dirac/blob/f3354c1f41cddb5c11d81a63ccbdac219bc3f92a/src/automation/dirac/automation.clj#L41
if you don’t need holistic view of a namespace then this could be a better way how to look at def’s metadata
Ok, interesting. It definitely looks possible then, thanks @darwin. And I guess I had totally missed that bit of defmacro too, thanks @danielcompton.