Fork me on GitHub
#clojurescript
<
2020-03-21
>
thelittlesipper03:03:06

Heyo, one of the few things I really enjoy about JavaScript is the ability to spread vars e.g.

let a = {"a": 1, "b": 2};
{...a, "c": 3};

-> {"a": 1, "b": 2, "c": 3}
Is such a thing available in ClojureScript land? I mean, I'm aware of merge and into and the like, but I think being able to spread from the child data-structure would be pretty neat-o, no? Esp. if it's already available in JavaScript - I don't see why not?

potetm03:03:32

I don’t understand what “spread from the child data-structure” means.

potetm03:03:44

This is merge.

coby03:03:55

I agree the spread operator is nice, but I don't think there's any special syntax for that. Clojure, and by extension ClojureScript, tend to eschew syntax sugar because it introduces complexity and makes the code-as-data model less composable.

potetm03:03:55

I have no idea what you would be able to do with additional syntax that you can’t do with merge.

hindol03:03:05

I think he wants something like apply.

coby03:03:48

The spread op can act like apply but for maps it really is just syntax sugar for merge

potetm03:03:55

yeah, I think operationally it’s like apply, but the only way you can do it is with merge

potetm03:03:08

literally just repeated Coby

coby03:03:41

It's an overloaded op in JS, it's apply for arrays, merge for objects

coby03:03:17

Anyway, the point is, Clojure is opinionated on this: just use a function.

thelittlesipper06:03:38

I see, and yes, it's the syntactic sugar that I was referring to as a nicety - ignore the "from the child data-structure" bit 🤐 lol. The unquote splicing syntax comes to mind as a similar feature in Clojure ~@. Here are some examples of what I mean, where v is some array/vector and m is some object/map: JS:

const a = [1, 2, 3, ...v];
const b = {"a": 1, "b": 2, ...m};
CLJS:
;; Current equivalent
(def a (into [1 2 3] v))

(def b (merge {:a 1 :2} m))

;; VS.

;; Just a couple ideas
;; As a js interop?
(def a [1 2 3 (... v)]) ; or [1 2 3 ... v]

;; Not sure what to do here without breaking maps and still keeping it concise, maybe metadata?
(def b {:a 1 :b 2 (... m)}) ; or {:a 1 :b 2 ... m}
I guess I was just thinking that merge, into, and apply have functional equivalents in JS land, but JS has this one lil' syntactic sugar ( ... ) that makes things a wee bit sweeter - not overall, just in this context - that CLJS doesn't have a congruent answer for. I'm really just nit-picking here, haha. But, I get it! That's simply not the way things revolve in this solar system - totally understandable. Just wanted to confirm 🙂

coby16:03:31

I knew what you meant about the child data structure. 🙂 And it has occurred to me that it'd be nice to extend ~@ to be applicable outside of macros. But it'd be introducing a fair amount of complexity and wouldn't really be JS interop, since you're still operating on CLJ data structures in all these examples, you're just making your CLJS look more like ES6. 😉 In most cases, even if you are operating on actual JS arrays/objects, you're probably not compiling down to version of JS which even supports ..., and actually the same is true of ES6 - it's not what gets run by the browser. All that said, the way you extend Clojure's syntax is mainly via macros (at compile time) and tagged literals (at parse time). In both contexts, you have to communicate to the parser/compiler that ... x means "treat x as several values to be spliced into the surrounding form," and unfortunately that's just not how it works. 😕 It's much simpler and less edge-case-y to operate on the surrounding structure as a whole, meaning it'd have to parse your op before any of the collection elements, so it knows what it's splicing into: (... {:a 1} m) ...at which point you may as well just use merge.

coby16:03:36

As an experiment I charged ahead and tried something like with tagged literals anyway, but it seems by the time it parses our map, it has already decided it's invalid:

user=> (def spread (partial apply list))
#'user/spread
user=> (spread 1 2 3 4 [5 6])
(1 2 3 4 5 6)
user=> (binding [*data-readers* {'... spread}]
  #_=> (read-string "(let [m {:c 3}] #... {:a 1 :b 3 m})"))
Execution error at user/eval25867 (form-init2755523084112094574.clj:2).
Map literal must contain an even number of forms

coby16:03:52

I'd recommend against magic like this in production anyway (unless you're doing something super domain-specific, tagged literals are probably not the right tool for the job), but hopefully it gives you an idea of what you might be able to do (even if in this case it didn't quite work 😛) https://clojure.org/reference/reader#tagged_literals

hindol07:03:50

Hmm, maybe [1 2 3 & v]? Just the way it works in destructuring?

👍 4
13tales08:03:30

Hey there. I want to start a clojurescript project, after not playing with Clojure in a couple of years. I’m mostly a frontend React dev, and haven’t really done much with Clojurescript before. Just wondering if someone can point me in a direction: what’s the current popular toolchain of choice for a Clojurescript/React project? Figwheel? Shadow-cljs? Is Reagent currently a good choice for a React-interface library, or something else? Appreciate your indulgence 🙏

hindol08:03:47

I was in the same boat. Think people are flocking to shadow these days.

hindol08:03:06

I recently did a toy project with Reagent, super easy! You can look at re-frame too. Don't know about popularity though.

13tales08:03:48

Thanks! Re-frame is something somewhat similar to Redux, right?

hindol08:03:32

I am no expert, but from what I read, yes.

13tales08:03:01

Thanks for the pointers 🙂

fricze11:03:05

1. use shadow-cljs. you’d be delighted. 2. re-frame/reagent is safe choice. it’s adopted, and tested in production widely enough. also, event-driven architecture is really pleasant to work with 3. if you fell less adventurous and want to use patterns known from React.js try out: https://github.com/Lokeh/helix or https://github.com/roman01la/uix 4. when you fell more adventurous try Fulcro. it might blow your mind, but it’s quite worth it

dpsutton15:03:58

if you want, you can check out https://github.com/dpsutton/asg-ignite-presentation. Its a 5 minute presentation that will make a react site and get it on netlify using npm deps and shadow-cljs. It has links to the presentation site, the site you will build, and the repo of the site you will build

hindol16:03:54

@U11BV7MTK Wow, really nice presentation. I am skimming through it and stumbled upon ^:dev/after-load. What does it do?

dpsutton16:03:44

one way to configure how to hotswap your app

coby16:03:15

+1 for using shadow-cljs, best tool out there for vanilla JS/React interop. (There's also a #shadow-cljs channel where the author hangs out.) The Re-frame README on GitHub is well-written and even entertaining, I'd recommend reading it all the way through. I'd also recommend trying Reagent out without Re-frame so when/if you do go with Re-frame you have a sense of what it's doing for you. 🙂

athomasoriginal19:03:41

Figwheel is still very much in use as well 🙂 If it helps, here is a breakdown of how I start my cljs projects: https://betweentwoparens.com/start-a-clojurescript-app-from-scratch Further, if you just want to quickly setup this structure check out this create-reagent-app template: https://github.com/athomasoriginal/create-reagent-app

13tales00:03:33

Thanks so much everyone. Awesome thread 👍

rich 8
💯 4