Fork me on GitHub
#clojurescript
<
2016-08-31
>
ag01:08:48

Reader conditionals still confusing me… this is much better from what I had before:

(:require #[email protected](: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?

dnolen01:08:13

@ag it’s for splicing in multiple forms

dnolen01:08:25

you clearly don’t want to stick a nested vector in there

shaunlebron05:08:49

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

ag05:08:36

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

shaunlebron05:08:32

@ag: what you had was fine then

shaunlebron05:08:27

oh, to answer your question about the “common” section, just put that outside the reader conditional 🙂

domgetter05:08:07

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?

domgetter05:08:36

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.

domgetter05:08:02

Eventually they agree (once the atom is updated) and the flickering stops.

domgetter05:08:54

Got it - Using defaultValue solves the issue

sivakumargsk08:08:32

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.

derwolfe13:08:51

Is there a better way in cljs to check if something is NaN than using (js/isNan val)?

darwin13:08:57

@derwolfe anything wrong with js/isNaN? just write your own helper fn wrapping it, to avoid js-interop in your normal code

derwolfe13:08:04

nope, nothing at all 🙂 Just want to see if there was a better way that didn't have js 🙂

lwhorton13:08:14

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?

selfsame13:08:43

(def nan? (fn [n] (and (number? n) (not (<= n Infinity)))))

darwin13:08:07

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

selfsame13:08:01

@derwolfe that would be if you want to check for an actual js/NaN

dnolen13:08:16

@lwhorton yes, that sounds unlikely

derwolfe13:08:40

thanks selfsame 🙂

lwhorton14:08:20

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.

lwhorton14:08:50

so the symbol-not-symbol? thing was perhaps munging the results from name/namespace

lvh14:08:16

@derwolfe Definitely js/isNaN; but you can also happily wrap that 🙂

nickt14:08:30

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?

nickt14:08:48

(defmacro blah [name & args] ...)

nickt14:08:59

but there exists a name builtin

nickt14:08:41

ah nvm i'll just use label

nickt14:08:21

hm.. other question: can I not use clj->js in a macro definition?

nickt14:08:31

I get a unable to resolve symbol error

selfsame14:08:27

@nickt don't think you can in clj, try something like

`(~'clj->js body)

anmonteiro15:08:54

@nickt: FWIW cljs.core/clj->js also works

nickt15:08:34

so I'm trying to write a macro that will recursively #js a nested Map structure

Tim15:08:15

interesting idea, I can see how that might be handy

nickt15:08:07

(defmacro expand [label obj]
  (let [expanded (clj->js styles)]
    `(def ~label ~expanded)))

nickt15:08:09

something like that

nickt15:08:27

but clj->js seems to not work in clj

nickt15:08:46

even if I (:require [cljs.core :refer [clj-js]])

nickt15:08:07

and if I do something like ('clj-&gt;js styles)` then the JS output after the cljs->js compile step tries to call core.cljs_-GT_js or something

nickt15:08:21

which I don't want, I want the js output to be an object literal

selfsame15:08:04

(defmacro to-js [form]
  `(cljs.core/clj->js ~form))

nickt15:08:16

@selfsame thanks. That's almost it... the js output I end up with includes a call to cljs.core.clj__GT_js.call

nickt15:08:37

which succeeds, but I don't want that call in the js output, I want the object inlined

selfsame15:08:30

oh i see, yeah not sure how to inline the reader #js

nickt15:08:22

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"}};

nickt15:08:44

the macro I'm trying to write would enable

nickt15:08:11

(defnested my-obj
  {:first-level
    {:second-level "hi"}})

nickt15:08:25

and yield the same my_ns.core.my_obj = {first: {second: "hi"}} inlined output

selfsame15:08:18

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.

selfsame15:08:01

if there's other ways to create js objects in a macro would also be interested in hearing them

darwin15:08:28

@nickt: look into js-obj, it should be a macro which expands to js object literal if it can

selfsame15:08:04

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"

nickt15:08:17

how could I recursively walk this cljs map literal and expand it into (js-obj "first-level" (js-obj "second-level" 5)) ?

selfsame15:08:48

@nickt well that's the fun part 🙂 You could try clojure.walk

nickt15:08:03

yea postwalk-replace looks like a good starting point

selfsame15:08:26

or a recursive fn that either returns form or (list 'js-obj (flatten (seq form)))

dnolen15:08:51

@selfsame that’s because js-obj has a macro case so it just inlines a literal, but it’s not recursive

dnolen15:08:01

cljs->js is runtime

dnolen15:08:07

and is recursive

selfsame15:08:27

heh, yeah my "wow" was realizing I can optimize a bunch of stuff on a project that's not recursive 🙂

dnolen15:08:51

right this is also what the #js literal is for

selfsame15:08:55

@dnolen but reader #js literals can't be used in a macro, right?

dnolen15:08:17

oh sorry, I lost the context - right

dnolen15:08:33

js-obj and array both have macro cases so they inline into literals

dnolen15:08:59

@nickt read time is before macro expansion time

nickt15:08:31

so I would have to transform map literals into nested (js-obj key value) forms

nickt15:08:35

at macro time

dnolen15:08:14

you have to be careful though, key must be a inline string, not some var or local

nickt15:08:17

yea i could just (name keyword) yea?

nickt15:08:59

What's a good environment to play around with this stuff?

dnolen15:08:43

you can macroexpand in a Clojure REPL, clojure.pprint does code formatting

dnolen15:08:34

Cider has macroexpand nice support, Cursive also if you’re using nREPL

nickt16:08:46

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)

nickt16:08:32

I was thinking something like this:

nickt16:08:47

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

nickt16:08:16

which would give me a list of ["foo" 2 "bar" 3] expressions

nickt16:08:45

or ["a" ["b" 1] "c" 2] in this case

nickt16:08:09

hm but @jr isn't that runtime?

nickt16:08:47

I'm trying to do this as a macro... I feel like I would have to return a quoted js-obj expression

jr16:08:06

correct

nickt16:08:18

so the doseq... aset part there isn't gonna fly for my case

jr16:08:10

copy the code and return a quoted js-obj form instead?

nickt16:08:51

how do I push things onto a quoted expression?

nickt16:08:03

(let [m `(js-obj)]
  (doseq [[k v] x]
    (push m (name x) value)
  m)

nickt16:08:11

sorry I know this is newbie but I'm super confused

selfsame16:08:12

cons should work

jr16:08:18

quoted expressions are just lists

jr16:08:22

`(js-obj [email protected](mapcat (fn [[k v]] [(name k) v]) {:foo "bar"}))

jr16:08:43

becomes (boot.user/js-obj "foo" "bar”)

jr16:08:13

so you’lll want to recursively build that list

selfsame16:08:19

@nickt spoilers: (defn ->js-obj [form] (cons 'js-obj (mapcat (fn [[k v]] (list (str k) (if (map? v) (->js-obj v) v))) form)))

nickt17:08:53

@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 [email protected](mapcat (fn [[k v]] [(name k) ~(map->js-obj v)]) x))
    x))

nickt17:08:09

When I put that in the repl though I get Cannot read property 'call' of undefined

selfsame17:08:51

quote js-obj 🙂

rlander17:08:33

Not really a Clojurescript question, but web related... does anyone know of a library for something like Django's Signals framework? Like observables/watchables?

nickt17:08:38

@selfsame I thought it was quoted with the backtick?

selfsame17:08:10

@nickt ~'js-obj or use cljs.core/js-obj, backtick stands for unquote

nickt17:08:51

@selfsame wait but @jr's example up there works without quoting js-obj

nickt17:08:09

I think it must be something about my ~(map->js-obj v) recursive call

selfsame17:08:44

so there's a difference between unquoting in cljs runtime and in a clj macro

nickt17:08:13

I'm trying to write a clj macro. Which form of quoting is appropriate?

selfsame17:08:11

you'll need the macro to emit valid cljs code, so you should check the macroexpansion while desigining it

jr17:08:40

@nickt: if you look closely at my example you’ll notice that the macro expanded to (boot.user/js-obj

jr17:08:00

js-obj is getting transformed into boot.user/js-obj when no namespace is specified

jr17:08:41

it should be

`(cljs.core/js-obj rest)

nickt17:08:13

@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 [email protected](mapcat (fn [[k v]] [(name k) ~(map->js-obj v)]) x))
    x))

nickt17:08:26

then I intend to use that def within a macro like this:

nickt17:08:11

(defmacro defstyles [label styles]
  (let [jsform (map->js-obj styles)]
    `(def ~label ~jsform)))

nickt17:08:30

as is, I get Attempting to call unbound fn: #'clojure.core/unquote

darwin17:08:41

you got it almost right, ~(map->js-obj v) should not be unqoted

darwin17:08:22

you are already in clojure land because of unqoute-splice [email protected]

selfsame17:08:46

ah I mispoke earlier in the convo, backtick is "syntax-quote" not unquote

nickt17:08:51

ooookay I'm so close now!

nickt17:08:53

thank you all so much

nickt17:08:06

one last hiccup:

nickt17:08:46

(defstyles whatever {:foo "bar"}) expands then compiles to js that looks like this:

nickt17:08:55

my_ns.core.whatever = (function(){ var obj9156 = {"foo": "bar"}; return obj91556;})();

nickt17:08:27

Is that just the compile behavior of (js-obj)?

nickt17:08:39

I really just want the object literal

darwin17:08:59

yes, that is produced by the implementation of js-obj, :advanced mode optimization should simplify it (my guess)

darwin17:08:40

if you want to have full control over emitted js, you could implement simplified version of js-obj macro, using js* directly

nickt17:08:05

@darwin hmm... where is js* defined so I can check how hard that would be? 😛

nickt17:08:24

ugh... in over my head

darwin17:08:41

definitely not an easy read...

jr17:08:54

(defmacro map->js [x]
  (json/write-str x))

nickt17:08:20

well now that's a thought..

nickt17:08:58

is this what you're talking about? https://github.com/clojure/data.json

darwin17:08:17

@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

nickt17:08:31

@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

nickt17:08:28

unfortunately that landed me in the dark magic of macros lol

nickt17:08:11

@darwin what would taht do?

darwin17:08:30

magic! try it 🙂

nickt17:08:25

I did, I got Attempting to call unbound fn: #'clojure.core/unquote 😕

darwin17:08:52

sorry fixed

nickt17:08:38

Unable to resolve symbol: js* in this context

nickt17:08:09

(defmacro map->js [x]
  (list 'js* "~{}" (json/write-str x)))

(defmacro defstyles [label styles]
  (let [jsform (map->js sty)]
    `(def ~label ~jsform)))

nickt17:08:23

oh hold on

selfsame17:08:31

@nickt IMO your finished macro is totally fine, the emitted js function closure wouldn't make a noticeable difference in perf or gc

nickt17:08:13

hm, nvm, I'm not sure how to solve that

nickt17:08:32

@selfsame yea totally. Unfortunately my end-goal is to produce statically-analyzable javascript

nickt17:08:07

and the IIFE's there make the AST traversal harder

nickt17:08:13

not impossible... but harder

darwin17:08:18

@nickt map->js must be a clojure function, not a macro

darwin17:08:50

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

darwin18:08:19

that is why it is complaining about js*, there is no such thing in clojure lands

nickt18:08:42

yea that worked, it produces a JSON string now

nickt18:08:17

my_ns.core.whatever = "{\"foo\":\"bar\"}";

darwin18:08:42

which is not what you wanted 🙂

stbgz18:08:46

has anyone tried running a node self-hostes cljs as a aws lambda service

nickt18:08:05

I'm not sure that's different than just using (let [jsform (json/write-str sty)]

nickt18:08:14

what's the js* "~{}" about?

nickt18:08:38

it's phenomenally close to what I want!

stbgz18:08:41

I think it would be a good fit since it would sidestep jvm startup time issues

darwin18:08:08

it is a "templating language" cljs compiler uses at lower levels, as I said, read the sources

nickt18:08:13

but it is a string and not an object literal, so it breaks AST traversal in js-land

darwin18:08:15

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 🙂

darwin18:08:01

@nickt: you will have to go back to previous js-obj solution, but instead generate raw js* templates

darwin18:08:17

for this you will need to implement a simpler version of js-obj macro

nickt18:08:55

where is js* defined? I'm having trouble grepping it

darwin18:08:03

I’m not sure, but it seems to be hard-coded in macroexpand-1*

noisesmith18:08:59

often things that end in * are special forms defined in the target's native language

noisesmith18:08:32

I wouldn't be surprised if js* was defined in javascript (but I don't know that it is)

darwin18:08:42

js* is very, very low-level and should not be used, unless you have no other way...

darwin18:08:11

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

selfsame18:08:35

interesting, always thought it was just a wrapper for (js/eval "1 + 1")

shaunlebron18:08:30

probably the clearest example is looking at the + macro implementation which uses js*

shaunlebron18:08:33

when + is used where it can be macro expanded, it will just emit a plus sign between the two operand expressions

shaunlebron18:08:48

and it uses js* for that

nickt18:08:57

@darwin what if I just forgo those whole map->js thing and use the #js reader?

nickt18:08:08

(defstyles whatever #js {:blah...})

nickt18:08:23

can I still do reduce-kv and stuff on the key/val pairs?

darwin18:08:06

@nickt: I’m not sure, analyzer+compiler will probably see JSValue type thing in place of #js …

darwin18:08:50

#js is interpreted by the reader, using custom data-readers defined by clojurescript compiler

nickt18:08:50

looks like a solid "no" 😛

nidu18:08:53

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.

darwin18:08:14

@nidu AFAIK requires are sorted topologically, exact ordering is not guaranteed

darwin18:08:21

also I would say cljs_deps.js has nothing to do with real execution of requires

darwin18:08:45

there are only declarations of deps, not order in which they are brought into the js context

nidu18:08:47

@darwin Yeah, forgot to mention - they depend explicitly. I guess that's the cause of error

nidu18:08:49

Yeah, making implicit dependency resolved the problem

darwin18:08:27

hmm, if your 2nd depends on 1st, then 1st will be loaded before 2nd

darwin18:08:50

explicit dependency of 2nd on 1st makes it a requirement

nidu19:08:32

oh my english. I wanted to say it's implicit

nidu19:08:42

sorry for this

nidu19:08:10

it was implicit and I made it explicit! 😃

darwin19:08:12

I’m confused 🙂 I think you said “implicit” and wanted to write “explicit"

darwin19:08:35

ok, understand

nickt19:08:26

hm looks like I can't even clojure.walk/postwalk-demo on a JSValue

darwin19:08:29

@nickt you should be able to, on val inside the thing: like (.-val thing)\

nickt19:08:07

@darwin what do you mean?

darwin19:08:29

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

darwin19:08:04

I’m sorry this is over my head, I cannot give you proper answers here

darwin19:08:40

could be (.val js-value-wrapper-thing), not sure

nickt19:08:22

(def swag #js {:foo "bar"}) followed by (.val swag) => cljs.user.swag.val is not a function

darwin19:08:49

where is your macro code?

darwin19:08:59

this can be seen only from macros

nickt19:08:44

oh interesting

nickt19:08:01

in my macro (walk/postwalk-demo (.-val styles)) didn't throw any errrors

nickt19:08:09

but also didn't print anything

darwin19:08:49

I’m sorry, cannot help anymore from top of my head without writing the code down - have some other work to do 🙂

nickt19:08:19

@darwin no problem thanks for sticking with me on this

nickt19:08:22

really appreciate your help

darwin19:08:27

np, good luck 🙂

nickt19:08:30

I've gotta be doing some other work now too but I'll fiddle around more later

timgilbert21:08:03

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?

darwin21:08:07

@timgilbert yes, you can, collect needed info via a macro and “export” it to cljs by emitting required data

darwin21:08:12

looking for an example

timgilbert21:08:35

Oh, interesting, thanks. Never seen &env before, is it like a cljs internal or something?

darwin21:08:00

I’m not sure, likely yes

darwin21:08:19

if you don’t need holistic view of a namespace then this could be a better way how to look at def’s metadata

timgilbert22:08:04

Ok, interesting. It definitely looks possible then, thanks @darwin. And I guess I had totally missed that bit of defmacro too, thanks @danielcompton.