clojurescript

ClojureScript, a dialect of Clojure that compiles to JavaScript http://clojurescript.org | Currently at 1.10.879
sergey.shvets 2021-06-16T00:02:23.237500Z

I see. I wasn't clear that I want to pass the def's symbol as one of the arguments to macro. I'm trying to write a macro that will build a query in compile time and trying to pass some meta information like names of collections, server, etc.

sergey.shvets 2021-06-16T00:03:37.237700Z

Meta information is constant (doesn't change in runtime) and I'm trying to figure out if there is a way to access it from macro

p-himik 2021-06-16T00:05:09.237900Z

Ah, then it's @(resolve symb). But the comment about it being in a CLJC (or regular CLJ) file still applies.

p-himik 2021-06-16T00:05:27.238100Z

symb has to be a fully qualified symbol, of course.

sergey.shvets 2021-06-16T00:09:24.238300Z

Thanks, it works with cljc file.

👍 1
West 2021-06-16T08:23:41.242400Z

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ójcik 2021-06-16T08:25:36.242500Z

You cannot get unwrapped result of the promise that way.

West 2021-06-16T08:26:23.242700Z

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

Karol Wójcik 2021-06-16T08:26:53.242900Z

Or any other way rather than:

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

Karol Wójcik 2021-06-16T08:27:55.243100Z

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ójcik 2021-06-16T08:28:23.243300Z

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

West 2021-06-16T08:30:05.243500Z

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.

West 2021-06-16T08:31:25.243700Z

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.

thheller 2021-06-16T08:31:26.243900Z

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

👍 1
❤️ 2
West 2021-06-16T08:31:58.244400Z

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

Karol Wójcik 2021-06-16T08:33:37.244600Z

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 :)

thheller 2021-06-16T08:35:11.244800Z

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

West 2021-06-16T08:36:07.245Z

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.

thheller 2021-06-16T08:36:38.245300Z

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

Karol Wójcik 2021-06-16T08:38:51.245500Z

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

West 2021-06-16T08:41:12.245800Z

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

thheller 2021-06-16T08:41:58.246Z

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

👍 2
Aron 2021-06-16T08:42:17.246200Z

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".

thheller 2021-06-16T08:42:22.246400Z

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

Aron 2021-06-16T08:43:20.246700Z

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

Karol Wójcik 2021-06-16T08:48:35.247Z

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

Aron 2021-06-16T08:58:55.247200Z

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

Aron 2021-06-16T09:01:37.247400Z

.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

👀 1
raspasov 2021-06-16T09:05:26.247800Z

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.

Aron 2021-06-16T09:08:00.248100Z

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.

raspasov 2021-06-16T09:09:17.248300Z

I would very much agree with @ashnur here

Aron 2021-06-16T09:11:33.248500Z

: ) 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ójcik 2021-06-16T09:11:45.248700Z

Quite huge 😄 to be honest.

Karol Wójcik 2021-06-16T09:13:28.249Z

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.

Aron 2021-06-16T09:13:30.249200Z

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

raspasov 2021-06-16T09:14:06.249400Z

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

raspasov 2021-06-16T09:15:36.249600Z

(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+.

Aron 2021-06-16T09:15:37.249800Z

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

Karol Wójcik 2021-06-16T09:15:57.250Z

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ójcik 2021-06-16T09:16:15.250200Z

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

Karol Wójcik 2021-06-16T09:16:30.250400Z

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

raspasov 2021-06-16T09:16:34.250600Z

@karol.wojcik what are fibers/async context?

raspasov 2021-06-16T09:17:28.251100Z

Oh… You’re using node?

Karol Wójcik 2021-06-16T09:17:48.251300Z

Unfortunately.

👌 1
raspasov 2021-06-16T09:17:49.251500Z

Sorry, zero experience there 🙂

Karol Wójcik 2021-06-16T09:18:29.251800Z

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 😄

raspasov 2021-06-16T09:18:33.252Z

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

raspasov 2021-06-16T09:19:11.252200Z

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).

Aron 2021-06-16T09:19:47.252400Z

Clojurescript backend?

raspasov 2021-06-16T09:20:02.252600Z

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

Karol Wójcik 2021-06-16T09:20:51.252800Z

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.

raspasov 2021-06-16T09:21:38.253100Z

Sounds complex 🙂

Karol Wójcik 2021-06-16T09:22:04.253300Z

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

raspasov 2021-06-16T09:22:28.253500Z

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

Karol Wójcik 2021-06-16T09:22:42.253800Z

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

Karol Wójcik 2021-06-16T09:23:12.254Z

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

Karol Wójcik 2021-06-16T09:23:34.254200Z

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

raspasov 2021-06-16T09:24:01.254500Z

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

raspasov 2021-06-16T09:24:07.254700Z

😜

raspasov 2021-06-16T09:24:45.254900Z

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

Karol Wójcik 2021-06-16T09:25:45.255100Z

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.

raspasov 2021-06-16T09:25:46.255300Z

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

Karol Wójcik 2021-06-16T09:27:42.255500Z

(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ójcik 2021-06-16T09:28:26.255700Z

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

raspasov 2021-06-16T09:28:33.255900Z

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.

raspasov 2021-06-16T09:30:14.256100Z

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.

raspasov 2021-06-16T09:31:17.256300Z

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

West 2021-06-16T09:31:36.256500Z

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

😆 1
Karol Wójcik 2021-06-16T09:31:48.256700Z

XD

raspasov 2021-06-16T09:33:01.257Z

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

thheller 2021-06-16T09:34:22.257200Z

@c.westrom 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

💯 1
Aron 2021-06-16T09:34:32.257400Z

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

raspasov 2021-06-16T09:35:09.257600Z

Yes, sorry @c.westrom 🙂 It was my bad.

Aron 2021-06-16T09:36:55.257800Z

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ójcik 2021-06-16T09:37:02.258Z

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.

West 2021-06-16T09:42:45.258400Z

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).

raspasov 2021-06-16T09:44:03.258600Z

@c.westrom 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.

West 2021-06-16T09:45:37.258800Z

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

raspasov 2021-06-16T09:45:39.259Z

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.

raspasov 2021-06-16T09:46:07.259200Z

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

raspasov 2021-06-16T09:47:12.259400Z

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 😝

raspasov 2021-06-16T09:47:56.259600Z

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

West 2021-06-16T09:48:29.259800Z

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.

raspasov 2021-06-16T09:48:59.260Z

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

West 2021-06-16T09:49:30.260300Z

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

raspasov 2021-06-16T09:49:58.260500Z

(defonce tmp-1 (atom nil) )

raspasov 2021-06-16T09:50:58.260700Z

(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-himik 2021-06-16T09:51:49.261Z

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

raspasov 2021-06-16T09:51:59.261200Z

^^^ yes

thheller 2021-06-16T09:53:11.261400Z

see also my answer to this recently https://stackoverflow.com/a/67853185/8009006

1
p-himik 2021-06-16T09:53:30.261800Z

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".

raspasov 2021-06-16T09:53:31.262Z

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.

thheller 2021-06-16T09:53:32.262200Z

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

thheller 2021-06-16T09:53:50.262500Z

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

thheller 2021-06-16T09:54:09.262700Z

but currently CLJS otherwise does not support async/await

👌 1
raspasov 2021-06-16T09:55:04.263Z

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

thheller 2021-06-16T09:55:50.263200Z

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

👍 2
thheller 2021-06-16T09:56:09.263400Z

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

👍 1
Simon 2021-06-16T09:56:27.263700Z

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

👀 1
Simon 2021-06-30T14:28:50.103500Z

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

Simon 2021-06-30T14:29:04.103800Z

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

West 2021-06-16T09:56:30.263800Z

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\"}}"

Simon 2021-06-16T09:57:09.264Z

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

West 2021-06-16T09:57:16.264200Z

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

🎯 1
raspasov 2021-06-16T09:59:53.264500Z

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

raspasov 2021-06-16T10:00:40.264900Z

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

raspasov 2021-06-16T10:01:08.265100Z

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

West 2021-06-16T10:02:55.265500Z

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)))))

thheller 2021-06-16T10:03:57.265700Z

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

✅ 1
thheller 2021-06-16T10:04:24.265900Z

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

West 2021-06-16T10:06:50.266200Z

(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.

West 2021-06-16T10:07:26.266400Z

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

West 2021-06-16T10:08:22.266600Z

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

raspasov 2021-06-16T10:08:47.266800Z

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

West 2021-06-16T10:10:12.267400Z

How convoluted…

😂 2
raspasov 2021-06-16T10:11:04.267700Z

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

raspasov 2021-06-16T10:11:29.267900Z

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

Aron 2021-06-16T10:11:31.268100Z

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

👍 1
raspasov 2021-06-16T10:12:06.268300Z

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

raspasov 2021-06-16T10:12:31.268500Z

(I came to Clojure from PHP around ~2014)

Aron 2021-06-16T10:12:35.268700Z

I have written php for many years

Aron 2021-06-16T10:12:44.268900Z

before js

West 2021-06-16T10:14:46.269100Z

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?

Aron 2021-06-16T10:15:19.269300Z

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.

👌 1
2021-06-16T10:21:25.269600Z

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

raspasov 2021-06-16T10:23:40.270Z

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 🙂

West 2021-06-16T10:25:50.270200Z

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

Aron 2021-06-16T10:26:59.270400Z

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

West 2021-06-16T10:27:30.270600Z

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

Aron 2021-06-16T10:27:32.270800Z

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

Aron 2021-06-16T10:27:48.271Z

probably just the name is new

West 2021-06-16T10:28:16.271200Z

What resources would you recommend I check out?

Aron 2021-06-16T10:28:24.271400Z

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

Aron 2021-06-16T10:28:51.271700Z

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

raspasov 2021-06-16T10:33:07.272400Z

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

Margo 2021-06-16T11:00:40.274200Z

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>}

Margo 2021-06-16T11:01:20.274300Z

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

West 2021-06-16T11:35:10.274900Z

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.

West 2021-06-16T11:38:32.275300Z

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?

thheller 2021-06-16T11:40:20.275500Z

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

Simon 2021-06-16T13:06:21.275900Z

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?

p-himik 2021-06-16T13:07:20.276200Z

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

Simon 2021-06-16T13:09:46.276400Z

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?

Margo 2021-06-16T13:49:02.276900Z

can you give me an example? @p-himik?

sansarip 2021-06-16T14:59:42.277100Z

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-himik 2021-06-16T15:03:54.277400Z

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

sansarip 2021-06-16T15:06:18.277600Z

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

sergey.shvets 2021-06-16T15:07:08.277800Z

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.shvets 2021-06-16T15:11:15.278100Z

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

bertofer 2021-06-16T17:00:30.280300Z

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

sergey.shvets 2021-06-16T17:01:01.280500Z

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

bertofer 2021-06-16T17:01:07.280700Z

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

dnolen 2021-06-16T17:02:12.281300Z

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

bertofer 2021-06-16T17:02:30.281700Z

Mainly for using ns-keywords aliased

dnolen 2021-06-16T17:02:35.281900Z

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

sergey.shvets 2021-06-16T17:02:37.282Z

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.

dnolen 2021-06-16T17:02:49.282400Z

you can not have a circularity in the graph ever

dnolen 2021-06-16T17:03:27.282900Z

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

dnolen 2021-06-16T17:04:10.283700Z

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

bertofer 2021-06-16T17:04:25.284Z

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

dnolen 2021-06-16T17:05:28.285Z

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

thheller 2021-06-16T17:08:26.286Z

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

bertofer 2021-06-16T17:09:54.286800Z

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

alexmiller 2021-06-16T17:20:15.287200Z

we have a plan for this for Clojure 1.11

👍 3
1
🎉 3
alexmiller 2021-06-16T17:22:00.288600Z

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

p-himik 2021-06-16T17:43:11.290Z

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

bertofer 2021-06-16T20:42:58.290800Z

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

sova-soars-the-sora 2021-06-16T23:54:09.291300Z

@simon.el.nahas you can do serverside rendering with Rum

sova-soars-the-sora 2021-06-16T23:55:06.291500Z

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-sora 2021-06-16T23:55:28.291700Z

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