This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-04-26
Channels
- # beginners (20)
- # cider (59)
- # cljsrn (6)
- # clojars (7)
- # clojure (91)
- # clojure-boston (1)
- # clojure-dusseldorf (3)
- # clojure-finland (1)
- # clojure-italy (8)
- # clojure-losangeles (1)
- # clojure-nl (16)
- # clojure-spec (25)
- # clojure-uk (113)
- # clojurescript (126)
- # core-async (27)
- # cursive (5)
- # data-science (3)
- # datomic (22)
- # emacs (24)
- # fulcro (30)
- # garden (7)
- # graphql (7)
- # leiningen (3)
- # nginx (1)
- # off-topic (63)
- # onyx (13)
- # portkey (1)
- # re-frame (1)
- # reagent (28)
- # shadow-cljs (92)
- # tools-deps (1)
- # uncomplicate (1)
- # vim (24)
- # yada (8)
Thanks!
No prob
I think I can make due with that. It seems like a nice pattern emerges with data structured as :company/name instead of {:company {:name …}}, --- things get flatter. If I have a lot of keys that I’m destructuring though and one is off then it seems like we aren’t able to let that be the exception and state it twice and everything else once. We have to state everything twice. 😕
You’re actually not committed to just that method of destructuring, if it helps - only the stuff that needs a different name
cljs.user=> (let [user {:user/keys {:id 56 :name "Jim Berlage" :cl "js"}}
#_=> company {:company/keys {:id 28 :name "Guaranteed Rate" :business "financial"}}
#_=> {{user-id :id :keys [name cl]} :user/keys} user
#_=> {{id :id company-name :name :keys [business]} :company/keys} company]
#_=> [user-id name cl id company-name business])
[56 "Jim Berlage" "js" 28 "Guaranteed Rate" "financial"]
You can still use :keys
, :as
, etc., and only specify that some variables have different names
Thanks…this is more than I knew was possible. Still, it seems it requires structuring a map? I’m getting this for user on line 1: #:user{:keys {:id 56, :name “Jim Berlage”, :cl “js”}}. My data would be more like {:company/name “foo” :user/name “bar}, so I’d be trying to destructure a single map with keys prefixed with e.g. “user/” and “company/” and rename the ones that clash between those namespaces. Maybe I just shouldn’t model my data that way
(fn [obj] (let [v1 (goog.object/get obj "k1") v2 (goog.object/get v1 "k2") v3 (goog.object/get v1 "k3")] ... ))
is there a better way to do this? when obj is #js {}
it crashes because goog.object/get
will return undefined but won't take undefined
right now my solution is to always use nil as default value, but something tells me I would forget and crash.. Not to mention very verbose
anyone thought about destructuring macro for js objects yet?
@poernahi try getValueByKeys https://google.github.io/closure-library/api/goog.object.html
Apparently getValueByKeys does not take nil as input while goog.object/get does. Very tricky!
Try (some-> v (goog.object/getValueByKeys #js["a" "b"]))
nice, and that actually covers nil and undefined
I've thought about a destructuring macro, yes, and I think it would be a good idea in principle (perhaps a js-obj aware version of let)
this works, but the tradeoff is verbosity when the key is optional. (.. #js {:a 1} -b -b)
throws for example, instead of returning nil. We then have to walk the structure one step at a time and nil/undef check on every step.
which is quite normal in js world, but cljs has get-in 🙂
is that an extension in shadow-cljs or available in the cljs compiler by default?
Another alternative is to extend object
to ILookup
and then you can do
(get-in obj [:k1 :k2 :k3])
@thheller I can't get it to work with pure cljs: https://github.com/pesterhazy/see-el-jay-ess/blob/master/src/my/core.cljs https://github.com/pesterhazy/see-el-jay-ess/blob/master/README.md
@mfikes interesing, wonder if there's any side effect?
Personally extending to ILookup feels risky
actually, it may not work when the js object is deliberately created without inheriting js/Object
@pesterhazy I agree. I've been trying it for a while to see if anything breaks.
@poernahi The make effect is to make get
work in the cond
branch that checks ILookup
. But I'd be curious if anything else goes wrong.
If we could get ^js obj
type hints to work generally, like @thheller suggested, that would be a big step forward
wrote about it here https://code.thheller.com/blog/shadow-cljs/2017/11/06/improved-externs-inference.html
that's a pity
(defn demo [obj]
(prn [(some-> obj (.-very) (.-foo))
(goog.object/getValueByKeys obj "very" "foo")]))
(defn demo2 [^js obj]
(prn [(some-> obj (.-very) (.-foo))
(goog.object/getValueByKeys obj "very" "foo")]))
(defn demo3 [^js/Object obj]
(prn [(some-> obj (.-very) (.-foo))
(goog.object/getValueByKeys obj "very" "foo")]))
(let [obj #js {"very" #js {"foo" "ok"}}]
(prn obj)
(println "demo")
(demo obj)
(println "demo2")
(demo2 obj)
(println "demo3")
(demo3 obj))
did a s/long/lengthy/ in my repo to rule out this issue
this also works with prototype-less obj (.create js/Object nil #js {:very #js {:value #js {:lengthy "ok"}}})
fun fact: since the demo2
and demo3
generate the externs demo
will also work. although externs inference still complains since it comes first.
so much magic around externs. Some property names are blacklisted so they don't trigger minification. Externs for one lib can influence minification for another part of your program...
initially I treid #js{"alpha" #js{"beta" "ok"}}
- that didn't minify for some reason
yeah externs only really work in fully typed programs. but CLJS isn't so any property defined in externs won't be renamed ever. thats why externs generators are so bad.
too many externs is totally fine by me
I worry about things randomly failing because I don't understand the logic behind when you need to annotate
yeah agreed. making it work is more important than making it small. that comes later 🙂
When I try :infer-externs true
with cljs.main
, that leads to a NPE: https://github.com/pesterhazy/see-el-jay-ess/blob/master/README.md#infering-externs
@thheller I've updated my test repo to include a non-nested case in addition to the nested map
With infer-externs false
property access returns nil
; with infer-externs true
it crashes.
@pesterhazy Even though you don't have a minimal repro, if your NPE stack is safe to put in a gist I'd be happy to take a quick look at it.
@mfikes it's a pretty minimal repro: https://github.com/pesterhazy/see-el-jay-ess/blob/master/scripts/run-test-deps
just clone and run that script
scripts/run-test-deps --infer
Looks like it might be filed https://github.com/google/closure-compiler/issues/2629
@mfikes, I wonder if it works for others, and if so what's special about my repro-repo
just the fact that I'm using cljs.main
?
@pesterhazy If you revise your deps.edn
to point at ClojureScript master, it no longer crashes, and the printed output seems to imply it it is working (I haven't read what that repo's code is doing). Specifically this coordinate seems to be OK
{:git/url "" :sha "132d3aa232921a3cea66f830d61c89be78c581cb"}
@mfikes I can confirm that it doesn't crash anymore, thanks for that!
two observations:
- with infer-externs
the type hints don't seem to matter, given that demo, demo2 and demo3 print the same output
- nested key access using (some-> obj (.-very) (.-lengthy))
doesn't seem to work
@pesterhazy This commit fixed it https://github.com/clojure/clojurescript/commit/30bc7c2ef251d74c2ab398c2a1461984f9c80469
that looks very plausible
By the way, as an aside, setting a :local/root
to ClojureScript lets you do a git bisect
directly on that repo to find what fixed or broke a project like Paulus's which uses deps.edn
. TL;DR deps.edn
made bisecting this trivial. 🙂
Isn't it great when a simplification (like using clj instead of lein) suddenly opens up unexpected possibilities?
(I didn't know, and if anyone else is wondering, :local/root
is explained here: https://clojure.org/guides/deps_and_cli#_using_local_libraries)
@mfikes out of curiosity, do you manually accept or reject a step when using git bisect
, or do you rely on the exit status of a script?
@pesterhazy Manually. Especially since I was running your repro to see if it worked or failed each time. It was only about 5 steps IIRC.
Thanks…this is more than I knew was possible. Still, it seems it requires structuring a map? I’m getting this for user on line 1: #:user{:keys {:id 56, :name “Jim Berlage”, :cl “js”}}. My data would be more like {:company/name “foo” :user/name “bar}, so I’d be trying to destructure a single map with keys prefixed with e.g. “user/” and “company/” and rename the ones that clash between those namespaces. Maybe I just shouldn’t model my data that way
you can destructure with something like {cname :company/name, uname :user/name, :company/keys [address], :user/keys [birthday id]}
etc
Thank you so much!! Exactly what I was hoping for:
(def m {:course/title "course title"
:lesson/title "lesson title"
:back-to-course "back to course"
:lesson/intro-paragraph "intro paragraph"
:course/header-image "header image"
:course/tags "tags"})
(let [{course-title :course/title
lesson-title :lesson/title
:course/keys [header-image tags]
:lesson/keys [intro-paragraph]
:keys [back-to-course]} m]
[course-title lesson-title header-image tags intro-paragraph back-to-course])
=> ["coure title" "lesson title" "header image" "tags" "intro paragraph" "back to course"]
Beautiful. Thank youWould this be good to add along with https://github.com/clojure/clojure-site/issues/265?
“this” == ?
If so and help is still wanted I can try
and go for it
“this” == destructuring namespaced kw maps when some unnamespaced versions clash, combining :keys
, :keys/foo
and {sym :ns/kw}
for brevity. I don’t see an example on the site that shows how they can be combined. Would you want one? I looked at the docs yesterday and wasn’t able to figure it out myself
certainly would be good in the guide
in reference page, my goals are a little different there
Ok. Will focus on the guide instead of the reference. What’s the difference between the two? Why should the reference not show those examples? Not disagreeing just trying to understand
the reference is … reference - it attempts to define all of the functionality and the way it can be composed. whereas the guide is “how do I use it” and may not necessarily be exhaustive.
I’m trying to add an npm dependency (`react-ace`) to my project (via :npm-deps
), but I’m getting an error:
index.js:20 Uncaught ReferenceError: module is not defined
at index.js:20
The error is in the following code:
var freeModule$$module$project_path$node_modules$lodash_isequal$index=freeExports$$module$project_path$node_modules$lodash_isequal$index&&"object"=="object"&&module&&!module.nodeType&&module
AFAICT that line is related to lodash.isequal
, which react-ace
depends on. Any idea what’s happening here?Thank you so much!! Exactly what I was hoping for:
(def m {:course/title "course title"
:lesson/title "lesson title"
:back-to-course "back to course"
:lesson/intro-paragraph "intro paragraph"
:course/header-image "header image"
:course/tags "tags"})
(let [{course-title :course/title
lesson-title :lesson/title
:course/keys [header-image tags]
:lesson/keys [intro-paragraph]
:keys [back-to-course]} m]
[course-title lesson-title header-image tags intro-paragraph back-to-course])
=> ["coure title" "lesson title" "header image" "tags" "intro paragraph" "back to course"]
Beautiful. Thank youyou’re missing a ] in calcs
:thumbsup:
@alex-dixon huh, this works?
(let [{:course/keys [header-image tags]} {:course/header-image "foo" :course/tags "bar"}] [header-image tags])
Why didn’t anyone tell me?! Where is this documented? 🙂Awesome right?
Was going to say the same thing lol. refactors whole app
Is it still the case that CommonJS modules won’t work properly with cljs?
@borkdude this is new in 1.9 and is doc’ed at https://clojure.org/reference/special_forms#_map_binding_destructuring
to be specific, the :course/keys stuff is new in 1.9. everything else is old.
but there is a ticket to improve that and extend the destructuring guide
I’m trying to google for the module is not defined
error I pasted above, and this is all I can find — trying to figure out if it’s out of date or not: https://stackoverflow.com/questions/35489797/using-react-components-in-reagent
@wombawomba not sure about npm-deps, but you can make it work pretty easily using the "double bundle" method: https://github.com/pesterhazy/presumably/blob/master/posts/double-bundle.md and https://github.com/pesterhazy/double-bundle
that's still my personal go-to method until all the cljs-node-module-require kinks are ironed out
until now I didn’t realize you can mix keys
with the other notation. I guess it’s just the unqualified version of the new feature:
(let [{a :a :keys [b]} {:a 1 :b 2}] [a b])
it makes so much sense, how could I not see it 😉multiple also supported:
(let [{:myns/keys [a] :keys [b] :myotherns/keys [c]} {:myns/a 1 :b 2 :myotherns/c 3}] [a b c])
:keys[ns1/v1 ns2/v2]
also possible.
You can also use :syms
it's a map from symbol to values and :strs
to strings.
yeah, none of that is new other than :myns/keys
yes, I thought they were exclusive and I managed to hold up that believe for years 😉
well, lots of people combine :keys and :as at the same time
but other than that, most people don’t do multiple kinds of associative destructuring in a single case
ah, yeah, nothing new there
that’s 1.0 stuff :)
increasing use of namespaced keys has put a lot of pressure on associative destructuring over the last few years
@pesterhazy cool, I’ll try that approach! thanks
Is there a function that creates a namespaced map or is it just the reader macro?
@alex-dixon The concept of “namespaced map” doesn’t really exist. The keys in a map can be namespaced.
in other words, this is a syntax - the objects are identical