clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
wombawomba 2021-04-14T12:45:14.159500Z

I'm walking over a data structure that contains JS "classes" like #object[Boolean] and #object[String]. How can I detect if an object is such a "class"? In regular Clojure I'd just use class?, but that's not available in cljs.

2021-04-14T12:54:17.159900Z

cljs.user=> (type [])
cljs.core/PersistentVector
cljs.user=> (type #js [])
#object[Array]
cljs.user=> 

nilern 2021-04-14T13:24:08.160100Z

cljs.user=> (instance? js/String (js/String.))
true
cljs.user=> (instance? js/String "")
false

😭 1
nilern 2021-04-14T13:25:57.160200Z

JS is awesome

wombawomba 2021-04-14T13:33:22.160800Z

I don't think that solves my problem though

wombawomba 2021-04-14T13:34:36.162300Z

How do I tell if something is #object[Boolean] or #object[String] or #object[Number] etc, as compared to a concrete instance of one of those classes, or to a Clojure object?

p-himik 2021-04-14T13:39:58.162800Z

Can you describe precisely what that "etc" means?

p-himik 2021-04-14T13:44:18.163Z

And what's the problem you're trying to solve in the first place?

wombawomba 2021-04-14T13:45:51.163200Z

I have a data structure that contains a random assortment of java classes and regular values/objects, or the corresponding objects in javascript (i.e. what we get via (class foo)), and I need to tell the classes apart from the concrete values

wombawomba 2021-04-14T13:46:32.163700Z

on the JVM this is trivial, because I can just use class?

wombawomba 2021-04-14T13:46:37.163900Z

but I'm not sure how to do it in JS

p-himik 2021-04-14T13:48:20.164200Z

Do you consider functions to be classes?

wombawomba 2021-04-14T13:48:44.164400Z

if it's what's returned from class, then yes

wombawomba 2021-04-14T13:49:30.164600Z

in practice I figure that this should be doable, because e.g. prn seems to be able to handle it fine

p-himik 2021-04-14T13:50:09.164800Z

Well, you're out of luck, it seems. In JS functions are those "classes".

16:48:03.106 class X {};
16:48:03.108 undefined
16:48:07.154 X
16:48:07.156 λ[]
16:48:10.762 typeof X 
16:48:10.764 "function"
16:48:41.318 function Y() {};
16:48:41.321 undefined
16:48:46.524 y = new Y();
16:48:46.526 Y {}

p-himik 2021-04-14T13:51:17.165Z

If your values cannot be functions, then you can just filter with fn?.

wombawomba 2021-04-14T13:51:17.165200Z

ew

p-himik 2021-04-14T13:51:22.165400Z

:D

wombawomba 2021-04-14T13:51:28.165600Z

they can be functions too though

p-himik 2021-04-14T13:51:48.165800Z

Welp. Unless you have a predefined set of "classes", I don't think you can solve that problem.

p-himik 2021-04-14T13:51:54.166Z

Or a predefined set of proper functions.

wombawomba 2021-04-14T13:51:58.166200Z

I guess I'll just try to enumerate all the classes I can think of and do an equality check

wombawomba 2021-04-14T13:52:14.166400Z

and cross my fingers that no other "classes" pop up

nilern 2021-04-14T14:52:04.166600Z

There are only String, Boolean and Number

wombawomba 2021-04-14T14:53:06.166800Z

oh, cool

p-himik 2021-04-14T14:56:22.167Z

@nilern What do you mean? What would be a common description for those?

nilern 2021-04-14T14:57:12.167200Z

Primitive wrappers

p-himik 2021-04-14T14:58:38.167400Z

I might be wrong, but I didn't get an impression that @wombawomba was talking specifically about those. I'm willing to be corrected though. :)

nilern 2021-04-14T14:59:05.167600Z

Woops there are more than three https://developer.mozilla.org/en-US/docs/Glossary/Primitive#primitive_wrapper_objects_in_javascript

nilern 2021-04-14T14:59:54.167900Z

bigint and symbol are quite new, thus my omissions

Franco Gasperino 2021-04-14T15:42:54.169600Z

What is the community recommendation on web component selection which supports react and is usable within clojurescript, preferably with the interopt abstracted away. I've tried both react-bootstrap and re-com with some success, but wanted a more informed feedback on direction

p-himik 2021-04-14T15:47:30.169700Z

I'm pretty sure the answer is basically the same as it would be in the JS community in general. Interop is not that much of a problem, but of course depends on the environment in which you'll be using web components. E.g. Reagent does most of the work for you.

raspasov 2021-04-14T15:48:38.169900Z

There’s also Helix, if you prefer a more direct React interop without much of state management.

Franco Gasperino 2021-04-14T15:49:18.170100Z

right now, im appreciating the re-frame model for state management, but am ignorant on best practices for the widgets/components

raspasov 2021-04-14T15:49:52.170300Z

Widgets/components as in “building your own” or “using existing” ones?

Franco Gasperino 2021-04-14T15:50:11.170600Z

i'm hoping to leverage existing libraries

p-himik 2021-04-14T15:50:42.170800Z

There are no best practices here, just like in JS. Some UI component libraries work for some people, some work for others. E.g. I chose Material UI specifically for two reasons - I like its documentation and its number input component is sensible (can parse scientific format).

raspasov 2021-04-14T15:52:14.171Z

Got it; You can just used them directly. I personally use my own judgement about the trade off between value provided and ability to change that component widget. Another question to ask is: “Is that component/widget at the right level of abstraction for the problem I’m solving?”

raspasov 2021-04-14T15:53:15.171200Z

For example, I’ve seen people building collaboration software and basing a big part of their product on a text editor widget; When you hit the limits/bugs of that widget, you’re stuck with a pile of complex code that nobody understands.

raspasov 2021-04-14T15:53:54.171400Z

On the other hand, picking a nice animation library that provides some fundamental animation abstractions can be a good choice.

nilern 2021-04-14T15:54:21.171700Z

I prefer CSS-only widgets, less fighting

raspasov 2021-04-14T15:55:06.171900Z

CSS-only? Most React components/widgets I’ve seen don’t fall in that category.

raspasov 2021-04-14T15:55:34.172100Z

Examples?

nilern 2021-04-14T15:55:39.172300Z

Well just CSS -> no React

raspasov 2021-04-14T15:56:10.172500Z

Got it 🙂 Yeah, haven’t done any development outside of React(Native) for a long time; can’t speak to that.

nilern 2021-04-14T15:56:52.172700Z

It's been a while, I've used Bootstrap and Bulma but not a huge fan of those either

nilern 2021-04-14T15:57:37.172900Z

I wanted to try https://getmdl.io/ at some time

raspasov 2021-04-14T15:57:44.173200Z

I see. I wonder how much value something like Bootstrap provides nowadays given that flex-box is widely supported.

Franco Gasperino 2021-04-14T15:57:55.173500Z

re-com is leveraging flex

Franco Gasperino 2021-04-14T15:58:13.173700Z

however, it usses css styling from bootstrap

nilern 2021-04-14T15:58:19.173900Z

In my mind widgets and layout are different feature sets

raspasov 2021-04-14T15:59:32.174100Z

Taking a quick look at re-com… @franco.gasperino have you used it?

Franco Gasperino 2021-04-14T16:00:28.174300Z

i've tried a bit of my internal prototype with re-com & re-frame. It works well, but lacks some widgets (navigation bars) that would be helpful

Franco Gasperino 2021-04-14T16:01:17.174500Z

i dont have any JS experience, so the removal of another tech stack simply for interopt is appreciated by me

raspasov 2021-04-14T16:03:11.174700Z

I don’t have much production JS experience either; Most of modern React-specific JS is relatively understandable, at least on the surface level; Digging into complex libraries/widgets is another story.

Franco Gasperino 2021-04-14T16:06:15.175100Z

are there any landmines which are known to the cljs community, specifically with regard to "avoid component library X", that I should know?

nilern 2021-04-14T16:06:47.175300Z

In any case one should make sure that the widget library is a net benefit. I have heard things like Material UI are risky in that regard (inflexibility, issues with Reagent etc.)

raspasov 2021-04-14T16:15:46.175700Z

@franco.gasperino I don’t think there’s a list that says GRAB (generally recognized as bad) 😄

Franco Gasperino 2021-04-14T16:16:24.175900Z

alright, i appreciate the feedback from the 3 of you

👍 1
raspasov 2021-04-14T16:16:29.176100Z

Perhaps I should make one! 😝

raspasov 2021-04-14T16:17:01.176300Z

generally = as by me

Franco Gasperino 2021-04-14T16:18:41.176500Z

ill stick with re-com for now. it mostly stays out of my way. the core of the SPA will be https://github.com/projectstorm/react-diagrams , while the re-com will handle the UI navigation bits

Dave A 2021-04-14T18:49:33.177800Z

@franco.gasperino I'm so happy you posted that link to react-diagrams, hadn't heard of it and it's exactly what I was considering having to DIY implement a bit down the line 😵

Franco Gasperino 2021-04-14T18:52:58.178Z

I don't know how easy it will be to integrate. I've done a reagent/adapt-react-class call or two, but beyond that ...

jacekschae 2021-04-14T19:14:56.178200Z

On the surface all of the React libraries will work via Reagent interop :&gt; and hopefully you are using shadow-cljs to do the npm imports. Beneath the surface there is a lot to unpack. Picking a UI library is not as simple as it was when only Bootstrap was around. Since then so many things happen and frontend community experimented in so many ways that it’s hard to keep up.  In general there are a couple of things you can use, I’ll categorize them in the following way but it’s just my point of view, don’t consider this as a ultimate: React + CSS; these libraries require npm install of a library and adding a CSS file to your <head />. • https://material-ui.comhttps://ant.design/docs/react/introducehttps://react-bootstrap.github.iohttps://blueprintjs.comhttps://react.semantic-ui.com Tradeoffs: • It’s CSS so it’s global thing and after some time you will be wondering can I remove this class or is it used anywhere? React Components + CSS-in-JS; these libraries require only npm install and CSS is generated by JS/CLJS, this solves the Cons of the React + CSS, because your style lives together with the component and not in the global namespace (CSS file).  • https://evergreen.segment.comhttps://rebassjs.org • All other libraries based on https://styled-components.com or https://emotion.sh/docs/introduction The way this works is that styles are dynamically generated at request time. Some libraries generate styles at build time so you would have to check. Tradeoffs: • Dynamically generated styles, which means you throw away caching. • Styled components need additional nesting of classes and they generate additional wrappers for your React elements which bloats your DOM tree and slows down the React diffing since the more your have in your tree the longer it takes; compare Google Chrome Dev Tools Elements vs Components tabs. • You bundle size will be slightly bigger because each component now has CSS definitions, on the other hand you don’t have CSS file. • Writing CSS in JS is sometimes painful, they rely heavily on template literals and if you don’t use shadow-cljs which proves some additional facilities to deal with that, you will suffer sometimes. • You need to name almost every single component, and naming is hard. Reagent + Atomic CSS; this is somehow new way to work with CSS, you have utility classes, sort of abstraction over CSS syntax, and as you can imagine your initial bundle size is huge since most of the libraries have new names for CSS. To work around this there is a https://postcss.org plugin that does tree-shaking for your CSS (removes unused styles from your CSS, think Google Closure compiler for CSS). There is also a new approach in tailwind-css where you have a JIT compiler to generate classes that are used in your files — check out this setup https://github.com/jacekschae/shadow-cljs-tailwindcss if you are interested how to get this going. https://tachyons.io https://basscss.com http://buildwithbeard.com https://turretcss.com https://tailwindcss.com Tradeoffs: • You end up with pretty messy CSS classes, some people don’t mind, some people hate it. Hope that helps.

raspasov 2021-04-14T19:22:52.178800Z

“The `String` constructor is used to create a new `String` object. When called instead as a function, it performs type conversion to a https://developer.mozilla.org/en-US/docs/Glossary/String, which is usually more useful.”

Franco Gasperino 2021-04-14T21:36:20.179400Z

Thank you, @jacek.schae

👍 1
Franco Gasperino 2021-04-14T22:59:30.181100Z

on the topic of best practices, what is the cljs equiv of slurp?

djblue 2021-04-14T23:10:21.181200Z

Depends on the cljs runtime, fs/readFileSync for node

Franco Gasperino 2021-04-14T23:10:46.181400Z

is there a generic approach to a browser-based runtime?

lilactown 2021-04-14T23:11:03.181600Z

generally browsers do not have access to the file system

☝️ 2
Franco Gasperino 2021-04-14T23:13:05.181900Z

in this use-case, i'm mocking out a json message which would be arriving to the runtime via an async call. I'm looking for a way to create the message locally for development.

djblue 2021-04-14T23:13:55.182100Z

(.resolve js/Promise data) would be how I would mock an async call

💯 1
Franco Gasperino 2021-04-14T23:15:26.182500Z

I dont need to mock the async bit, i need to mock the actual content. (-> (slurp) (js/JSON.parse) (js->clj) -> ... throw onto channel)

Franco Gasperino 2021-04-14T23:15:42.182700Z

it's the slurp equiv i'm attempting to replicate for mocking

djblue 2021-04-14T23:19:03.182900Z

If the content you are expecting from slurp is a json string, you could mock slurp with (constantly "&lt;json string&gt;")

Franco Gasperino 2021-04-14T23:20:29.183100Z

makes sense. was looking for a way to maintain the string outside of the code for this case. I'm running a number of mock content inputs against a schema validation, and they're getting to be overbearing to define all as values in code

Franco Gasperino 2021-04-14T23:23:11.183300Z

(def mock-element
  {:id (str (random-uuid))
   :name "mock-element-1"
   :description "This is a mock"
   :repo "configured-repo-1"
   :type :input
   :widget :square
   :config {}})

(defn edn-&gt;json [content]
  (-&gt; content
      stringify-keys
      clj-&gt;js
      (js/JSON.stringify)))

(edn-&gt;json mock-element)

Franco Gasperino 2021-04-14T23:23:24.183500Z

would like to move mock-element out to local file for testing

Franco Gasperino 2021-04-14T23:24:43.183700Z

(granted what i pasted is the inverse of the question, but illustrative nonetheless)

Franco Gasperino 2021-04-14T23:28:00.183900Z

(defn json-&gt;edn [content]
  (-&gt; content 
      (js/JSON.parse)
      js-&gt;clj
      keywordize-keys))

lilactown 2021-04-14T23:30:05.184100Z

this isn't possible to do at runtime because browsers, where your code is running, do not have access to the file system

Franco Gasperino 2021-04-14T23:30:19.184300Z

dang

lilactown 2021-04-14T23:30:38.184500Z

there is a way to do this in shadow-cljs, but I don't really know that it's any better then keeping the EDN in a CLJS file?

Franco Gasperino 2021-04-14T23:33:09.185100Z

The FileReader class is strictly for node (@djblue suggestion) or in-memory payloads from forms or blob arrays, then?

lilactown 2021-04-14T23:34:22.185300Z

right

djblue 2021-04-14T23:34:30.185500Z

You could also do this with a clj macro, https://github.com/djblue/portal/blob/master/src/portal/resources.cljc I how I do it

lilactown 2021-04-14T23:35:36.185800Z

right, you can read in the file at compile time but the naive version will not reload on changes to those files. this can get annoying and complex if you change those files at all often.

💯 1
lilactown 2021-04-14T23:36:05.186Z

the reloading problem is solved by the shadow-cljs helper in the link I posted

lilactown 2021-04-14T23:36:36.186200Z

still, if you are set on moving these outside of code that is the best approach

lilactown 2021-04-14T23:37:23.186500Z

btw, portal is very very cool 🙂

💯 1
❤️ 1
Franco Gasperino 2021-04-14T23:46:39.186800Z

thanks for the input