Fork me on GitHub
#clojurescript
<
2020-10-01
>
Aron10:10:46

How does nested destructuring work with js objects? E.g. resizeobserver entries' contentRect's width and height?

p-himik11:10:30

Destructuring doesn't work with JS objects at all.

Aron11:10:57

I think it does

p-himik11:10:18

Are you sure you have a plain JS object and not some bean or something?

cljs.user=> (let [{:keys [a]} #js {:a 1}] a)
nil
cljs.user=> (let [{a "a"} #js {:a 1}] a)
nil

p-himik11:10:54

Or maybe you have extended js/Object to support ILookup. It may work but don't do that.

Aron11:10:48

I just realized I don't even know how to start a REPL for cljs. All the examples talk about something else when there is mention of this :-<

p-himik11:10:38

A one-liner:

clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "RELEASE"}}}' -m cljs.repl.node

Aron11:10:58

thank you, much appreciated. I think there should be some alias saved for this, but couldn't find yet

p-himik11:10:43

I actually just added it to ~/.clojure/deps.edn myself. :) Was using http://clojurescript.io before.

Aron11:10:10

ClojureScript 1.10.773
cljs.user=> (def obj #js [{"contentRect" {"width" 1 "height" 2}}])
#'cljs.user/obj
(defn example [[{{:strs [width height]} "contentRect"}]]
  [width height])
#'cljs.user/example
cljs.user=> (example obj)
[1 2]
cljs.user=> 

Aron11:10:40

haven't tried it with the actual resizer entry yet, had to put it together step by step, very hard to understand what is going where

p-himik11:10:25

Ah, there's your problem. #js is shallow.

p-himik11:10:41

Huh, and apparently destructuring works with JS arrays.

Aron11:10:18

I didn't know #js is shallow, will fix that

Aron11:10:10

so I should use a bean or something

p-himik11:10:20

Or just use plain getters, like .-prop or from goog.object.

Aron11:10:16

it's 5 lines to write them all out

Aron11:10:35

most of it is not relevant at all.

Aron11:10:34

I can't make it work with beans either. Must say that while js is literally the same thing everywhere, and I can run a webserver in a browser if I really want it without changing the source, clojurescript is nothing like clojure, the two seem to be two entirely separate languages and I keep tripping over this, even after years of first realizing it.

p-himik11:10:51

Just in case - you can chain the usages of .-prop:

cljs.user=> (def x #js {:a #js {:b 1}})
#'cljs.user/x
cljs.user=> (.. x -a -b)
1

p-himik12:10:29

> I can run a webserver in a browser Really? How would it listen for new connections?

p-himik12:10:08

CLJS is very similar in this regard to CLJ - you access properties of the native objects with the dot syntax. I'm not sure what the difference is that you're tripping over.

p-himik12:10:36

CLJ and CLJS are indeed almost the same language if you stay within the language itself and don't try to venture into the interop world. The only exception that I can think of right now is how macros work. And I think a few :require might be different.

Aron12:10:04

I didn't say I can listen to new connections, I said I can run the code 🙂 Doesn't have to make sense

Aron12:10:33

the difference is that I've been trying to write this thing for hours and I still don't know how it works

p-himik12:10:15

OK, let's agree to disagree re web servers then. :) What exactly are you having troubles with?

cljs.user=> (require '[cljs-bean.core :refer [bean]])
nil
cljs.user=> (def x #js {:a #js {:b 1}})
#'cljs.user/x
cljs.user=> (let [{{:keys [b]} :a} (bean x :recursive true)] b)
1

Aron12:10:23

basically, while the language has gazillion tools to make your life easy, if you are working with javascript, it's kind of a gambling experience to find the right combination that actually does what I expect

p-himik12:10:33

It has a very well defined behavior and a very limited set of very composable things. I fail to see how this could possibly be a gamble.

Aron12:10:31

I don't like the agree to disagree phrase, it doesn't mean what people think it means. But let's not delve into that. 🙂 I am registering a resizer observer event on an element and I would like to get the width and height properties from under the contentRect property. The only way I could do it to write .-propertyaccess way, none of the other syntax approaches or even using cljs-bean seem to have access to those values

Aron12:10:44

I do not question that it's well defined! 🙂

p-himik12:10:26

Can you be more specific - maybe you have a link to the JS code that you would like to convert to CLJS?

Aron12:10:43

just a min

Michaël Salihi12:10:04

I like this library's approch for destructuring js obj, lookup, an more. Very useful: https://github.com/applied-science/js-interop

Aron12:10:39

but this is the thing, why do i need additional dependencies to do something that's so basic?

Aron12:10:18

it increases the incidental complexity of the project so much so early.. it's really disheartening

Michaël Salihi12:10:47

For eg., i used it on one CLJS React native project because I wanted to manipulate some JS obj idiomatically.

p-himik12:10:45

@U0VQ4N5EE map-like destructuring is for things that support get, that's it. You can have some object that both supports get, has some value associated with :x, and has an .-x property. If get would work on JS properties, this situation would be ambiguous.

p-himik12:10:51

Interop is different than "vanilla" CLJ[S] code, that's it. But the amount of specifics is so negligible that any cheatsheet gives you an extensive information with all the nuances.

Aron12:10:08

you definitely like to say "that's it" 🙂

Aron12:10:16

The question is, how to get from javascript objects something that can be destructured? You asked for the example, have you checked it out?

p-himik12:10:00

I can confirm that cljs-bean does not work in this case because the entries that the callback receives are not POJOs. But it's how this particular library works and it's not a fault of CLJS. > how to get from javascript objects something that can be destructured? If you really need that functionality, some library or custom code has to be used as CLJS does not have that built-in (neither does CLJ). And it seems that cljs-bean does not satisfy the criteria.

Aron13:10:32

I didn't say that cljs is at fault, I hope that's not what transpired... I am particularly careful to not associate blame with anything...

p-himik13:10:52

Well, you did say "clojurescript is nothing like clojure" although in this particular context they're exactly the same. And that "[the lack of JS destructuring in CLJS] increases the incidental complexity of the project so much so early". And while it may be somewhat true compared to starting a pure JS project, it's for a very good reason and I don't think there could be any other way without making CLJS a mess.

Aron13:10:59

> in this particular context they're exactly the same. does this mean that clojure is unable to destructure POJOs the same way clojurescript apparently is? I didn't say the lack of destructuring causes the increase incidental complexity, why do you accuse me of such nonsensical things so I have to explain myself for things I never said? The increase in incidental complexity is if and only if I have to include additional dependencies to achieve something. It was a specific answer to a specific suggestion, not a general comment about clojurescript. Do you have so many people making such bad claims that you misunderstand me so much, or is it just me who you treat like this?

p-himik13:10:53

> does this mean that clojure is unable to destructure POJOs the same way clojurescript apparently is? Yes. > The increase in incidental complexity is if and only if I have to include additional dependencies to achieve something You have to include additional dependencies (or write some code) to support JS objects destructuring. Without doing anything, you cannot destructure #js {...}. The lack of destructuring of POJOs makes you having to include something to support that destructuring, exactly as you say. Given that, I don't know what you're disagreeing with. I'm not trying to point fingers here, I'm pointing at what I see as inconsistencies in reasoning.

Aron13:10:27

> > does this mean that clojure is unable to destructure POJOs the same way clojurescript apparently is? > Yes. Thanks, that's good to know.

mhuebert10:10:19

FWIW, js-interop at least attempts to provide an api that is consistent with clojure(script). There are macros that enable destructuring (on anything js, not only POJOs), and there is j/lit which like a recursive #js. There is j/get, j/get-in, j/assoc! and so on, which all follow clojure semantics. I do agree that this stuff is nuanced and easy to run into issues. As “the ship has sailed” on what ClojureScript provides out-of-the-box, libraries/user-space is the place to look now. cljs-bean is very cool and useful for situations where wrapping makes sense, I like js-interop for the rest of cases (eg. non-POJOs or perf-sensitive stuff), where you want the compiled code to be essentially what you’d write in javascript.

👍 3
Aron01:10:12

I think I will use js-interop instead of bean, but only when I absolutely need either

lepistane12:10:00

low resolution questions here 😞 is it possible to run CLJS SPA app within someones website? kinda like widget? does it have to be in iframe? could compiled CLJS code be called from site's JS code and work properly?

p-himik12:10:21

Since your last question seems to be able to answer every other one, I'm gonna answer just it. Yes, it can be called from a third-party JS code and work properly.

p-himik12:10:49

Export all the needed symbols, create a JS bundle, and use it as a regular JS script.

lepistane12:10:05

are there any examples online that i can refer to?

lepistane12:10:18

or tutorials?

p-himik13:10:41

Any CLJS tutorials would do since I don't think there's anything special about using some code in a compiled CLJS bundle from some JS.

p-himik13:10:46

Imagine you have two separate JS files that both augment js/window. How would you go about using some function from the second file in the code from the first file? Same exact thing here because compiling CLJS will give you a single JS file.

lepistane13:10:01

makes sense thank u!

Michael Stokley15:10:42

it looks like clojurescript doesn't throw arity exceptions, just compile warnings. (i suppose that's because js doesn't care.) do folks try to be disciplined in terms of arity anyway, or should we treat this like a feature and embrace it? what's more idiomatic?

Michael Stokley15:10:38

i guess the fact that it's a compile warning, and not an error, indicates something about community preferences

ghadi15:10:55

@michael740

node
Welcome to Node.js v14.12.0.
> function foo(a){return 42;}
undefined
> foo("one")
42
> foo("one", "two")
42

ghadi15:10:12

it's inline with the JS platform

Michael Stokley15:10:59

@ghadi - given that, i take it that it's idiomatic to ignore arities when convenient? the re-frame subs come to mind. reg-sub expects a fn of type [db query-v] -> ? but maybe it's convenient to pass in (fn [db] ...) instead

p-himik15:10:50

I try to not do that. I think shadow-cljs gives warnings when you use incorrect arities. Well, at least when it can deduce the issue.

Michael Stokley15:10:56

i'm curious about this because it feels like a type error. does the clojurescript community not mind in much the same way that the larger clojure community doesn't get that worked up over types?

p-himik15:10:11

I can't speak for the whole community, but I don't remember seeing any CLJS code that would deliberately use incorrect arities. IMO abusing "fluid arities" is just asking for trouble.

isak15:10:37

I don't think that will cause any problems, because I don't see how you could enforce arities in ClojureScript without a big performance penalty

p-himik16:10:30

It will cause problems as soon as some library decides to introduce an arity that you've been using mistakenly, like the (fn [db] ...) example above. Re: check - not sure. It's a single check of arguments.length > N.

isak16:10:12

I thought he was talking about just passing callbacks that accept less arguments than they will receive. I can't see how that would cause any problems regardless of what the library that invokes the callback does. Can you?

isak16:10:32

> It's a single check of `arguments.length > N`. It would still mean wrapping a lot of function calls though (all the function calls you can't verify statically). That means bigger bundle size too.

p-himik16:10:32

> I thought he was talking about just passing callbacks that accept less arguments than they will receive. I can't see how that would cause any problems regardless of what the library that invokes the callback does. Can you? Absolutely. A third-party lib expects a 2-arg fn, but you pass a 1-arg fn because you don't care about the second arg. After a while, the lib adds support for 1-args fns that are called in different circumstances than their 2-arg buddies. Usually such a change would be considered a non-breaking one. But your code could easily be broken by it, regardless of whether it's a callback or not.

isak16:10:53

How would they check the arity of your function in order to call it in different circumstances?

p-himik16:10:36

The lib doesn't check arity, it checks something else:

(case my-condition
  7 (callback-fn 1)
  42 (callback-fn 1 2))

isak16:10:26

Hmm, I don't see why that would be a problem in your function there. They'd have to pass something else in as the first argument, right? And that seems like it would be a breaking change.

p-himik16:10:54

Right, my example is just to show that it doesn't check the arity. But the first argument should be different, you're right. It wouldn't be a breaking change because before the lib fn was explicitly expecting a 2-arg fn. A lib user, by using a 1-arg fn, would break the contract and reap the consequences some time later.

p-himik16:10:38

If something expects something specific, it's better to not do something else just because it may be easy. JS makes many things excruciatingly easy. So much so there's a feet deficit.

isak16:10:09

I think I see your point. But adding more arities, then changing the order of arguments for those new arities (or similar) seems like it should be considered a breaking change for a ClojureScript library (since it would be easy to make this mistake)

p-himik16:10:01

Ah, right - the order may be the same. My example is still a good one because there's one more possibility. That third-party lib might expect different results from the callback fns with different artities. Like map - the 1-arg one returns a transducer but the 2-arg one returns a collection.

p-himik16:10:41

That way it wouldn't really be a "callback fn", but whatever - still some function that does something, only this time it also should return something.

isak16:10:37

Ok yea that is a good point

wilkerlucio19:10:12

hello, while implementing a custom map type in CLJS, I noticed that the contains? fn doesn't use the -contains-key? from IAssociative. I'm making the impl on both CLJ and CLJS, on CLJ it uses the -contains-key?, is this a bug on CLJS or is there are reason for it?

wilkerlucio20:10:07

by looking at sources, the impl of contains? in CLJ is very different, the CLJS one just relies on get, while the CLJ has a bunch of type checks

p-himik20:10:16

Just in case - there's also #cljs-dev that might be better at getting attention of those who know.

wilkerlucio22:10:28

thanks, I'll post there as well 🙏