Fork me on GitHub
#clojurescript
<
2021-06-16
>
West08:06:41

How might I change this function so that I get a js-object in my repl as opposed to this promise? I have no idea how javascript really works, I just thought I could slap a few functions together.

Karol Wójcik08:06:36

You cannot get unwrapped result of the promise that way.

West08:06:23

Ok, is there any way to avoid promises altogether? I really don’t want to overcomplicate fetching json in the browser.

Karol Wójcik08:06:53

Or any other way rather than:

(-> (get-json all-posts-url) 
    (.then (fn [posts] (def all-posts posts)))

Karol Wójcik08:06:55

It's not over-complication. It's how Javascript works and I would rather stick to promise, promesa. Alternatively you can use Clojurescript channels.

Karol Wójcik08:06:23

But still the result of fetching will be a channel and not a plain value.

West08:06:05

You know, I’m not attached to using js/fetch. Is there a library that abstracts this away so I can just do something like (slurp url). Maybe a bit naive, but I figured I’d ask anyway.

West08:06:25

Honestly if there is no abstraction already I’m just gonna make my own, because I’m not too sure it’s worth learning what promises are just for getting a few lines of json.

thheller08:06:26

that is technically impossible in JS. all IO is async and there is no way to get a sync result from that.

❤️ 6
👍 3
West08:06:58

Awww man. Well then I guess I’m learning how asynchronous stuff works.

Karol Wójcik08:06:37

I mean in certain situations it's possible when using Node.js Fibers. Caveats are: • it's experimental • works only in Node.js Your best bet is to learn the asynchronous nature of Javascript :)

thheller08:06:11

node does have some special sync methods but yes those do not exist in the browser

West08:06:07

I’m gonna look into using https://github.com/clojure/core.async then. Seems to me that this is the more idiomatic way of doing async programming in clojure/script.

thheller08:06:38

if its just one function core.async is probably overkill but it is certainly a good option

Karol Wójcik08:06:51

Core.async is overkill for almost every situation in the browser environment.

West08:06:12

I’ve never done any asynchronous programming before, so maybe this will be easier to learn.

thheller08:06:58

it'll be much harder learning core.async and using it correctly than just a promise with a .then

👍 6
Aron08:06:17

Using core async you can actually have composable backpressure, and when you want to do composable state management where the state in question comes from remote/async io then what would you use? Any reasonable js app will have such situations. The fact that you can use go blocks and have your data written as if it was sync, not async, kinda matters. Yes, if you squint and ignore all the design issues with the javascript Promise API, then that also looks a bit like sync ordering, but it's not even comparable to CSP. I used js-csp and other csp libs in js since 2015 because it's just so much easier to maintain and develop with it than with any other async "solution".

thheller08:06:22

well worth learning it IMHO but still something to be aware off 😉

Aron08:06:20

CSP is just algebra, you learn the algebra and you are fine ☺️

Karol Wójcik08:06:35

I don't get what is the issue with PromiseAPI in Javascript world, could you please elaborate @U0VQ4N5EE?

Aron08:06:55

It has a history that I have experienced first hand, even before https://promisesaplus.com/ became a thing

Aron09:06:37

.then is overloaded, it does both map and flatmap, I don't consider that a good idea until recently it was relatively difficult to cancel promises and many js devs don't even know how serious that issue is there is also an alternate perspective, not mine, that criticizes it differently https://avaq.medium.com/broken-promises-2ae92780f33

👀 3
raspasov09:06:26

The one downside of core.async is that the output code is somewhat larger, esp when using (go …) blocks. Otherwise, the paradigm is solid and has been very useful to me in many use cases, esp. in the browser/JS world.

Aron09:06:00

A well written async flow with CSP can be modified at any place, can be extended anywhere, and I would say it's both simple and easy compared to what you get with Promises.

raspasov09:06:17

I would very much agree with @U0VQ4N5EE here

Aron09:06:33

: ) Thanks, I can also agree that it can be overkill, but on the other hand, what is the production size difference if you do not use core.async?

Karol Wójcik09:06:45

Quite huge 😄 to be honest.

Karol Wójcik09:06:28

I'm developing quite important and big e-commerce site which extensively uses core.async (mostly for fetching). Full core async + the code it generates takes ~460kb. This is the only issue I have with core.async.

Aron09:06:30

I am genuinely interested, because before cljs, when I had to support old browsers, the js build was the same size as cljs with core async

raspasov09:06:06

Another benefit of core.async is that it will work as far back as IE6, if I’m not mistaken.

raspasov09:06:36

(I mostly use it for ReactNative, so the output difference is not a big deal for me) My CLJS output at the moment is 1.5MB. The app itself is 30MB+.

Aron09:06:37

460kb full application doesn't seem to be much, probably because I am used to material-ui bloating my build

Karol Wójcik09:06:57

Because of some early decisions we are also using fibers and async context. Core async without patching leaks the context, so we had an issue in which session would jump between the users.

Karol Wójcik09:06:15

460kb is the size of core.async + code it generates in the application.

Karol Wójcik09:06:30

Full application (together with lazy modules) is around 6mb.

raspasov09:06:34

@UJ1339K2B what are fibers/async context?

raspasov09:06:28

Oh… You’re using node?

Karol Wójcik09:06:48

Unfortunately.

👌 3
raspasov09:06:49

Sorry, zero experience there 🙂

Karol Wójcik09:06:29

No worries 😄 I have plenty experience with Node.js that's why I know that fibers + core.async + asynchrounous context is a bad design choice 😄

raspasov09:06:33

So is that a problem with core.async that appears specifically on node? Or is the problem in node itself?

raspasov09:06:11

Everything I was saying, I was referring to client-side core.async (browser and/or ReactNative). No experience with core.async on the server (node).

Aron09:06:47

Clojurescript backend?

raspasov09:06:02

I have used core.async on server, but only Clojure JVM.

Karol Wójcik09:06:51

The problem is that if you master/worker architecture and you use core.async in fibers to populate an asynchronous context, then you can be pretty sure that the little functions which are made by core.async are not wrapped in fiber and as a result you have a leaking context.

raspasov09:06:38

Sounds complex 🙂

Karol Wójcik09:06:04

It's complex. Nowadays I do avoid core.async even for simpler stuff.

raspasov09:06:28

Rrright. But that sounds like a problem that’s specific to node + core.async situation.

Karol Wójcik09:06:42

The problem with build size which I mentioned earlier on is real 😄

Karol Wójcik09:06:12

You can see how much code core.async generates under the hood.

Karol Wójcik09:06:34

It's 2x/3x code generated by async/await.

raspasov09:06:01

Just (set! print-fn-bodies true) and evaluate a (go …) in the REPL

raspasov09:06:45

(not sure if that would shrink after :advanced though)

Karol Wójcik09:06:45

Exactly 😄 The most of the code in the application is fetching. I have like 100 functions which fetch something and all of those use core.async. Of course that for 1-10 functions difference is negligible, but scale matters.

raspasov09:06:46

(set! print-fn-bodies true) true ar.main=> (fn [] (a/go (+ 1 1)))

Karol Wójcik09:06:42

(require '[cljs.core.async])
(cljs.core.async/go (<! (cljs.core.async/chan 1)))
var c__9__auto___47 = cljs.core.async.chan((1));
cljs.core.async.impl.dispatch.run(((function (c__9__auto___47){
  return (function (){
    var f__10__auto__ = (function (){var switch__4__auto__ = ((function (c__9__auto___47){
      return (function (state_40){
        var state_val_41 = (state_40[(1)]);
        if((state_val_41 === (1))){
          var inst_36 = cljs.core.async.chan((1));
          var state_40__$1 = state_40;
          return cljs.core.async.impl.ioc_helpers.take_BANG_(state_40__$1,(2),inst_36);
        } else {
          if((state_val_41 === (2))){
            var inst_38 = (state_40[(2)]);
            var state_40__$1 = state_40;
            return cljs.core.async.impl.ioc_helpers.return_chan(state_40__$1,inst_38);
          } else {
            return null;
          }
        }
      });})(c__9__auto___47))
    ;
                                     return ((function (switch__4__auto__,c__9__auto___47){
                                       return (function() {
                                         var cljs$user$state_machine__5__auto__ = null;
                                         var cljs$user$state_machine__5__auto____0 = (function (){
                                           var statearr_42 = [null,null,null,null,null,null,null];
                                           (statearr_42[(0)] = cljs$user$state_machine__5__auto__);
                                           (statearr_42[(1)] = (1));
                                           return statearr_42;
                                         });
                                         var cljs$user$state_machine__5__auto____1 = (function (state_40){
                                           while(true){
                                             var ret_value__6__auto__ = (function (){try{while(true){
                                               var result__7__auto__ = switch__4__auto__.call(null,state_40);
                                               if(cljs.core.keyword_identical_QMARK_.call(null,result__7__auto__,new cljs.core.Keyword(null,"recur","recur",(-437573268)))){
                                                 continue;
                                               } else {
                                                 return result__7__auto__;
                                               }
                                               break;
                                             }
                                                                                        }catch (e43){var ex__8__auto__ = e43;
                                                                                                     var statearr_44_48 = state_40;
                                                                                                     (statearr_44_48[(2)] = ex__8__auto__);
                                                                                                     if(cljs.core.seq.call(null,(state_40[(4)]))){
                                                                                                       var statearr_45_49 = state_40;
                                                                                                       (statearr_45_49[(1)] = cljs.core.first.call(null,(state_40[(4)])));
                                                                                                     } else {
                                                                                                       throw ex__8__auto__;
                                                                                                     }
                                                                                                     return new cljs.core.Keyword(null,"recur","recur",(-437573268));
                                                                                                    }})();
                                             if(cljs.core.keyword_identical_QMARK_.call(null,ret_value__6__auto__,new cljs.core.Keyword(null,"recur","recur",(-437573268)))){
                                               var G__50 = state_40;
                                               state_40 = G__50;
                                               continue;
                                             } else {
                                               return ret_value__6__auto__;
                                             }
                                             break;
                                           }
                                         });
                                         cljs$user$state_machine__5__auto__ = function(state_40){
                                           switch(arguments.length){
                                             case 0:
                                               return cljs$user$state_machine__5__auto____0.call(this);
                                             case 1:
                                               return cljs$user$state_machine__5__auto____1.call(this,state_40);
                                                                  }
                                           throw(new Error('Invalid arity: ' + arguments.length));
                                         };
                                         cljs$user$state_machine__5__auto__.cljs$core$IFn$_invoke$arity$0 = cljs$user$state_machine__5__auto____0;
                                         cljs$user$state_machine__5__auto__.cljs$core$IFn$_invoke$arity$1 = cljs$user$state_machine__5__auto____1;
                                         return cljs$user$state_machine__5__auto__;
                                       })()
                                       ;})(switch__4__auto__,c__9__auto___47))
                                    })();
    var state__11__auto__ = (function (){var statearr_46 = f__10__auto__.call(null);
                                         (statearr_46[cljs.core.async.impl.ioc_helpers.USER_START_IDX] = c__9__auto___47);
                                         return statearr_46;
                                        })();
    return cljs.core.async.impl.ioc_helpers.run_state_machine_wrapped(state__11__auto__);
  });})(c__9__auto___47))
                                 );
You can check this out: http://app.klipse.tech/

Karol Wójcik09:06:26

This is mostly what we are doing. Where 1 is a call to function which returns channel.

raspasov09:06:33

I agree that if the task is easy, you can avoid core.async. But there are a few cases where it really shines, IMO. Fine grain animations that also need to be cancellable is one. Another is loading a lib that takes time to be “ready” and having a bunch of places in the code that need to wait for that event.

raspasov09:06:14

You do get a lot of value, but there’s a cost (big output size and some weird edge cases/bugs). I am quite happy that a few of the long time outstanding bugs got recently fixed on the CLJS side thanks to the latest CLJS fixes.

raspasov09:06:17

For example, (and …) (or …) were kinda broken inside (go …) loops but now work correctly and have proper termination semantics.

West09:06:36

Man, why does this stuff have to be so complex? I just want to consume a url in the browser.

😆 3
raspasov09:06:01

🙂 Last example of something that would be, IMO pretty hard without core.async, but with it it looks easy.

thheller09:06:22

@U01KQ9EGU79 don't get distracted by this discussion. you don't have to make it complicated. You already had all the code you need in the last snippet you posted. just process the all-posts in the .then callback instead of trying to put it into the def

💯 3
Aron09:06:32

I was not aware that node.js has issues with such build sizes

raspasov09:06:09

Yes, sorry @U01KQ9EGU79 🙂 It was my bad.

Aron09:06:55

and it does seem to be the exact situation which I am always afraid of, one thing in which we are overcommitted lead us down a path where each step is a reasonable choice at the time, but in the end we are doing something that if we knew ahead of time that it's a possibility, we could've chose a different path, possible one that overall better, even subjectively

Karol Wójcik09:06:02

When I was referring to build sizes I meant browser, sorry if it was not clear from my previous statements. We have an isomorphic application.

West09:06:45

Ok, so just to recap, here’s my function.

(defn get-json
  [url]
  (->
   (js/fetch url)
   (.then (fn [res] (.json res)))
   (.then (fn [json] (.stringify js/JSON json)))
   (.catch (fn [err] (.log js/console err)))))
I don’t want to console.log the response, but rather have it show up in my repl, when I run (get-json url).

raspasov09:06:03

@U01KQ9EGU79 I think the recap is that you… cannot. That would be a synchronous IO operation, and JS (at least in the browser) does not have that.

West09:06:37

There’s no way at all to abstract this as a synchronous operation?

raspasov09:06:39

All you can do with that response is call another function or save it somewhere (like a CLJS atom). Saving it in an atom even temporarily can be useful for debugging and development in the REPL.

raspasov09:06:07

No. That’s a JS environment thing. CLJS can do nothing about that.

raspasov09:06:12

The “best” you can do is a core.async (go …) block 🙂 That gives you the illusion of synchrony. But it is an illusion. And we’re back to square 1 in this thread 😝

raspasov09:06:56

Or JS async/await (also an illusion of synchrony). But AFAIK, CLJS doesn’t really support async/await at the moment @U05224H0W yes?

West09:06:29

Ok, this data will be consumed by another function. Can I just call (get-json url) and pass it as an argument? I’m having a hard time reasoning about this. Would be nice to see what data comes out before I write a function to consume it.

raspasov09:06:59

^^ yes save it an atom and play around with it at the REPL. Do you have a functioning CLJS REPL setup?

West09:06:30

Yeah I’ve got my repl. So I’ll create an atom and save the data there then… somehow. Let me try.

raspasov09:06:58

(defonce tmp-1 (atom nil) )

raspasov09:06:58

(defonce tmp-1 (atom nil))

(defn get-json
 [url]
 (->
  (js/fetch url)
  (.then (fn [res] (.json res)))
  (.then (fn [json]
          (let [json' (.stringify js/JSON json)]
           (reset! tmp-1 json'))))
  (.catch (fn [err] (.log js/console err)))))

p-himik09:06:49

Note that the above should be used only for debugging and not in any production application.

p-himik09:06:30

Once you start using async stuff, you cannot get its results in a non-async context. You will have to make it async as well, in one way or another - core.async, promises with callbacks, async/await, whatever. The only thing a sync function can do to an async one is just "fire and forget".

raspasov09:06:31

I mean, there’s a case for saving the resp in an atom for production, but overall I agree 🙂 The cases are few. This is an exploratory snippet/approach.

thheller09:06:32

but yeah the temporary atom can make it work in the REPL

thheller09:06:50

maybe I'll add support for some kind of await in the CLJS REPL at some point

thheller09:06:09

but currently CLJS otherwise does not support async/await

👌 3
raspasov09:06:04

I actually have never written any JS code with async/await. That’s also… an illusion of synchrony of some sort, right?

thheller09:06:50

yeah, turns every place you use it into async. works pretty much exactly like the core.async go macro

👍 6
thheller09:06:09

just doesn't need to do all the rewriting since its supported natively

👍 3
West09:06:30

Ok, evaluating @temp-1:

Class: java.lang.String
Value: "{\"content\":\"<div><h1>Here's some stuff</h1><ul><li><p>Here's more stuff\\n</p></li><li><p>more stuff\\n</p></li></ul></div>\",\"data\":{\"title\":\"Lorem Ipsum\",\"subtitle\":\"stuff test\",\"date\":\"2021-04-15\",\"tags\":\"test\",\"author\":\"Christian Westrom\",\"id\":\"test-1\"}}"

West09:06:16

This is good, I think I’ll just make sure it’s an actual js object within get-json.

🎯 3
raspasov09:06:53

For conversion to/from JS objects, https://github.com/mfikes/cljs-bean is a good choice.

raspasov10:06:40

js->clj
clj->js
Also work but cljs-bean lib is faster.

raspasov10:06:08

Usually it doesn’t make a big difference, until it does.

West10:06:55

Since I don’t need .stringify couldn’t I refactor the original function like this?

(defn get-json
  [url]
  (->
   (js/fetch url)
   (.then (fn [res] (let [json' (.json res)]
                       (reset! temp-1 json'))))
   (.catch (fn [err] (.log js/console err)))))

thheller10:06:57

no. .json returns another promise since its an async operation. so you need the (.then (fn [json] (reset! temp-1 json))

3
thheller10:06:24

or (reset! temp-1 (js->clj json))

West10:06:50

(defn get-json
  [url]
  (->
   (js/fetch url)
   (.then (fn [res] (.json res)))
   (.then (fn [json] (reset! temp-1 json)))
   (.catch (fn [err] (.log js/console err)))))
Ok, that seems to work.

West10:06:26

Ok, so since this is asynchronous, I basically have to chain my functions using (.then)?

West10:06:22

Or, in other words, pass functions into a (-> (.then (fn …)))` ?

raspasov10:06:47

Yes. (.then …) takes a function which receives the result of the previous promise.

West10:06:12

How convoluted…

😂 6
raspasov10:06:04

Async is not for the faint of heart, that’s for sure.

raspasov10:06:29

(any async: core.async, promises, etc)

Aron10:06:31

lol, in actual existence, where anything that matters is, everything is async

👍 3
raspasov10:06:06

Yes… and no. You can write PHP and barely touch any async 😃

raspasov10:06:31

(I came to Clojure from PHP around ~2014)

Aron10:06:35

I have written php for many years

Aron10:06:44

before js

West10:06:46

Ok, if I’m not assigning this data to a variable, do I have to go to my endpoint and change it so it’s async?

Aron10:06:19

but that's not what I meant. There are 3 main categories where things that can be named through language (human or machine): 1. our inner subjective experience which is primal, 2. language where we relate parts of our subjective experience and communicate it 3. the shared part of the communication which we assume is independent of the subjective and thus, objective. What matters is either in the 1. or 3. domains. What is in the 2nd domain, the interface layer, is the least important. Both 1 and 3 are async. 2. pretends that it's sync.

👌 3
raspasov10:06:40

I guess in nature, nothing is really synchronous, as far as we know (limited by the speed of light). That reminds of the Are We There Yet? talk by Rich Hickey. Perhaps moving into #off-topic 🙂

West10:06:50

Alright, this has got to be bad style, but it’s gonna take awhile for me to really understand how this stuff really works.

Aron10:06:59

if you are running code on a javascript vm, you have to know about the event loop

West10:06:30

Ok, I have no idea what the event loop is.

Aron10:06:32

when i do async stuff, even if I do it with promises, I try to organize them as smaller analogues to the event loop

Aron10:06:48

probably just the name is new

West10:06:16

What resources would you recommend I check out?

Aron10:06:24

https://www.youtube.com/watch?v=8aGhZQkoFbQ this is browser, and if you do multithreading in node, it's different

Aron10:06:51

for single threaded stuff, it should be more or less the same

raspasov10:06:07

Curiously, I just learned the synchronous calls DO exist but are deprecated (and I don’t think anybody who’s serious about web/js dev uses them anymore): https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests

West11:06:10

Ok, I just wanna really ground this in something I have working already. This is a stripped down version of my frontend.

(ns app.core
  (:require [clojure.string]
            [reagent.core :as r]
            [reagent.dom :as r.dom]
            [reitit.frontend :as rf]
            [reitit.frontend.easy :as rfe]
            [reitit.coercion.spec :as rss]
            [app.data :refer [data blog-posts]]))

(defn blog-preview-page
  []
  [:div {:class '[flex-grow]}
   (for [blog-post blog-posts]
     ^{:key (-> blog-post :data :id)}
     [blog-post-preview (:content blog-post)]
     )
   ])

(defn app
  []
  [:div {:class '[]}
   [navbar]
   (if @match
     (let [view (:view (:data @match))]
       [view @match])
     (.log js/console
           (str "Match not found.\n `(:data @match)`:"
                (:data @match))))])

(defn mount-root
  [component]
  (r.dom/render component (.getElementById js/document "app")))
I have this reagent component blog-preview-page. What do I have to do in order for it to interact with my asynchronous fetch? Usually I would just call it as another value from app.data or elsewhere.

West11:06:32

It seems like the solution will ultimately involve coupling fetch with my frontend, I’d like to avoid that. So the other solution involves using atoms. Is this really it?

thheller11:06:20

yes, you use an atom to store the result. where you run the fetch depends on your app needs. you can just do it once on startup, or with some timer or other conditions

West00:06:35

(defonce blog-posts (r/atom '()))

(defn get-json
  [uri]
  (->
   (js/fetch uri)
   (.then (fn [res] (js->clj (.json res) :keywordize-keys :true)))
   (.catch (fn [err] (.log js/console err)))))

(-> (get-json all-posts-uri)
    (.then (fn [posts]
             (for [post posts]
               (let [post-uri (uri post)]
                 (->
                  (get-json post-uri)
                  (.then (fn [post] (swap! blog-posts conj post)))))))))
Ok, that was a bit hard to reason about, but this seems to work. This whole callback business makes me have to think of things backwards in a way.~ Very weird. Note: the uri and all-posts-uri are not in the snippet.

Simon09:06:27

Is there anything like Next.js for ClojureScript SPAs? Hosting and SSR?

👀 3
Simon09:06:09

or could I use Next.js for a ClojureScript app?

hkjels10:06:25

From what I can tell, next.js is a mix of things, but mainly it tries to make best-practices more accessible through some pre-compile steps. In ClojureScript, we have macros, so it would work more naturally AFAIK, but you would also likely not find a framework like next. You can maybe find libraries for the individual features though

Simon13:06:21

The key feature from Next.js I want is Server Side Rendering (SSR) and hosting. How do i get that in ClojureScript? I am aware that i could create my own node.js server, but it is just not as easy as with Next.js?

Simon13:06:46

Since CLJS compiles to JS i can imagine that I could compile to a Next.js project, where I am using the Next.js Routing library instead of React Router. Who inside the clojurescript commnunity could know about this?

sergey.shvets15:06:08

I haven't used next.js but unless they do some trickery with source code (like custom syntax, babel, etc.) you should be able to use it with cljs. You would probably need something like Hicada to build your React components, so there are no extra layers that can throw next.js optimizations off. Another way, is to use clojure back-end to generate HTML on server-side. Rum is an example, where they just use JVM to generate HTML from hiccup (I don't think hydration works in this way). Also, I think om/reagent can do something similar. I don't like either of those approaches and currently trying to sketch my own solution on top of firebase. CLJS offers some very cool capabilities with core.async and macros and I think eventually it can be better and simpler than next.

sergey.shvets15:06:15

I had no problems running clojurescript code in firebase functions, so any nodejs webserver + hiccup should work with no problem (like express)

sergey.shvets17:06:01

Also, fulcro is an option but requires quite a lot of learning before you can get started.

sova-soars-the-sora23:06:09

@U024A5W9WBG you can do serverside rendering with Rum

sova-soars-the-sora23:06:06

You have a .clj server, a .cljs SPA, and a .cljc file that hosts the shared components written in rum. You include all the shared components to the .clj and the .cljs, and the rendered artifact (html) must be identical to be able to up-strap the SSR with your SPA.

sova-soars-the-sora23:06:28

There are alternatives but I find rum to be the cleanest wrapper on react and the most straightforward way to achieve SSR.

Simon00:06:10

Thank you!

Simon14:06:50

for future reference, I found this template for shadow cljs https://github.com/thheller/next-cljs

Simon14:06:04

I haven't tested it yet, but maybe I will soon

Margo11:06:40

Might be a bit of a dumb question, but how do you interop js destructuring statement such as this one into ClojureScript?

const DefaultElement = props => {return <p {...props.attributes}>{props.children}</p>}

Margo11:06:20

specifically <p {...props.attributes}>{props.children}</p> in hiccup notation.

p-himik13:06:20

You just pass the attributes directly since Hiccup expects a map or a JS object (IIRC).

Margo13:06:02

can you give me an example? @U2FRKM4TW?

sansarip14:06:42

A translation to hiccup could look like the following:

(defn default-element [attributes & children]
  [:p attributes (into [:<>] children)])

;; Usage as hiccup
[default-element {:style {:color :blue}}
  [:p "I'm a child!"] 
  [:p "Another child!"]]

p-himik15:06:54

No need for the fragment. Just (into [:p attributes] children).

sansarip15:06:18

True! The fragment is there as a matter of opinion not requirement.

bortexz17:06:30

Is there any way to get something like (alias ) in clojurescript, without reqiuring the namespace? Mainly for using aliased namespaced keywords

bortexz17:06:07

I have circular dependencies for the specs namespaces, so cannot really require them

sergey.shvets17:06:37

you should be able to use fully qualified name, but you should definitely resolve circular dependencies, as it will backfire even if it builds now.

dnolen17:06:12

@bertofer not currently possible - circular deps won't build so I'm not sure what you are trying to do

bortexz17:06:30

Mainly for using ns-keywords aliased

dnolen17:06:35

as in alias won't fix anything - it will just break

dnolen17:06:49

you can not have a circularity in the graph ever

dnolen17:06:27

which isn't to say alias wouldn't be useful - but it cannot be used to fix circular deps if they are submitted to the build

dnolen17:06:10

unless you're saying they aren't submitted to build

bortexz17:06:25

I mostly want to avoid using hardcoded long namespaces for keys, I don’t need to require the namespace. I have ns’s only for specs, and there I would like to have “circular” references, but only between specs

dnolen17:06:28

ah k - then no - not currently possible - but would be an useful patch - probably medium difficulty though

thheller17:06:26

there is https://clojure.atlassian.net/browse/CLJ-2123. solution still pending for both CLJ and CLJS

bortexz17:06:54

thanks both, think I will look for a different approach for now and keep an eye on that issue, as in clj it’s also a bit “hacky” for the circular deps

p-himik17:06:11

You can simply (def my-kw :some.long.namespace/my-keyword) as a workaround or even a proper solution.

bortexz20:06:58

that would work, though I prefer to use kw directly. For now I will go with the (alias create-ns) approach with reader macro for only clj, declare specs only in clj, then cljs can require those ns only for aliasing keys to access those fields in maps

Alex Miller (Clojure team)17:06:15

we have a plan for this for Clojure 1.11

👍 8
3
🎉 8
Alex Miller (Clojure team)17:06:00

for CLJ-2123 that is, I haven't read all the backchat to know whether it would address the original question