Fork me on GitHub
#clojurescript
<
2018-03-16
>
Josh Horwitz00:03:21

Hey All, coming back to ClojureScript. What is the best way to get started now? Looking for good book/videos

Garrett Hopper02:03:48

When trying to use cljs.loader, the compiler knows the namespace exists, but then it starts trying to look for it in my main.out directory. Any ideas?

Compiling ClojureScript...
• main.js
                                       java.lang.Thread.run                  Thread.java:  748
         java.util.concurrent.ThreadPoolExecutor$Worker.run      ThreadPoolExecutor.java:  624
          java.util.concurrent.ThreadPoolExecutor.runWorker      ThreadPoolExecutor.java: 1149
                        java.util.concurrent.FutureTask.run              FutureTask.java:  266
                                                        ...
                        clojure.core/binding-conveyor-fn/fn                     core.clj: 2022
                              adzerk.boot-cljs/compile-1/fn                boot_cljs.clj:  160
                                   adzerk.boot-cljs/compile                boot_cljs.clj:   72
                                          boot.pod/call-in*                      pod.clj:  413
                                                        ...
org.projectodd.shimdandy.impl.ClojureRuntimeShimImpl.invoke  ClojureRuntimeShimImpl.java:  102
org.projectodd.shimdandy.impl.ClojureRuntimeShimImpl.invoke  ClojureRuntimeShimImpl.java:  109
                                                        ...
                                          boot.pod/call-in*                      pod.clj:  410
                                      boot.pod/eval-fn-call                      pod.clj:  359
                                         clojure.core/apply                     core.clj:  657
                                                        ...
                         adzerk.boot-cljs.impl/compile-cljs                     impl.clj:  154
                                       cljs.build.api/build                      api.clj:  205
                                         cljs.closure/build                  closure.clj: 2775
                               cljs.closure/compile-sources                  closure.clj:  979
                                         clojure.core/doall                     core.clj: 3140
                                         clojure.core/dorun                     core.clj: 3134
                                          clojure.core/next                     core.clj:   64
                                                        ...
                       cljs.closure/compile-sources/iter/fn                  closure.clj:  983
                                          cljs.closure/fn/G                  closure.clj:  511
                                            cljs.closure/fn                  closure.clj:  648
                              cljs.closure/compile-from-jar                  closure.clj:  624
                                  cljs.closure/compile-file                  closure.clj:  558
                                 cljs.compiler/compile-file                compiler.cljc: 1523
                              cljs.compiler/compile-file/fn                compiler.cljc: 1563
java.io.FileNotFoundException: The file /data/prs/boot/cache/tmp/data/prs/dev/amazon/2x0/-tgmalg/main.out/cljs/loader.cljs does not exist.
   clojure.lang.ExceptionInfo: The file /data/prs/boot/cache/tmp/data/prs/dev/amazon/2x0/-tgmalg/main.out/cljs/loader.cljs does not exist.
    from: :boot-cljs
   clojure.lang.ExceptionInfo: The file /data/prs/boot/cache/tmp/data/prs/dev/amazon/2x0/-tgmalg/main.out/cljs/loader.cljs does not exist.
    line: 287

Garrett Hopper03:03:37

Hmm, it seems like it was the clojurescript version I was on (`1.10.126`). I'm back on 1.9.946 now, and that isn't the problem, though I'm running into a possible google closure bug now?

Uncaught TypeError: Cannot read property 'EvaluateCodeEvent' of undefined
    at goog.module.ModuleLoader.evaluateCode_ (:3000/main.out/goog/module/moduleloader.js:228)
    at goog.module.ModuleLoader.handleSuccess_ (:3000/main.out/goog/module/moduleloader.js:256)
    at goog.net.BulkLoader.goog.events.EventTarget.fireListeners (eventtarget.js:284)
    at Function.goog.events.EventTarget.dispatchEventInternal_ (eventtarget.js:381)
    at goog.net.BulkLoader.goog.events.EventTarget.dispatchEvent (eventtarget.js:196)
    at goog.net.BulkLoader.finishLoad_ (:3000/main.out/goog/net/bulkloader.js:168)
    at goog.net.BulkLoader.handleSuccess_ (:3000/main.out/goog/net/bulkloader.js:138)
    at goog.net.BulkLoader.handleEvent_ (:3000/main.out/goog/net/bulkloader.js:118)
    at goog.net.XhrIo.goog.events.EventTarget.fireListeners (eventtarget.js:284)
    at Function.goog.events.EventTarget.dispatchEventInternal_ (eventtarget.js:381)

qqq08:03:42

I need a way to turn Hiccup into HTML Dom nodes. Is reagent the way to go, or is there a separate library?

joelsanchez08:03:59

reagent will do it just fine

gklijs09:03:31

@qqq you can also use the html function, and use Google closure turning a string into a Dom element. No need to use reagent just for that I think.

benzap09:03:42

yeah this, I think I have a vanilla javascript solution somewhere

gklijs09:03:31

I did it like this, using functions from goog.dom

(defn node-from-data
  [data]
  (gdom/safeHtmlToNode (legacy/safeHtmlFromString (html data))))

(defn set-html
  ([data] (set-html data nil))
  ([data parent-id] (set-html data parent-id true))
  ([data parent-id remove-childs] (set-html data parent-id remove-childs nil))
  ([data parent-id remove-childs place-at]
   (let [new-node (node-from-data data)
         node-id (.-id new-node)
         current-node (if (nil? node-id) nil (ensure-element node-id))]
     (if current-node
       (gdom/replaceNode new-node current-node)
       (if-let [parent (ensure-element parent-id)]
         (do
           (if remove-childs (gdom/removeChildren parent))
           (if place-at
             (gdom/insertChildAt parent new-node place-at)
             (gdom/append parent new-node)))
         (log (str "could not place html: " data " on parent: " parent-id)))))))

joelsanchez10:03:11

first implementation of cljs (411 lines), and Rich's org-mode document rambling about motivations, state of the project, and its future https://github.com/clojure/clojurescript/tree/515900f9762102987bda7d53b919dafc0b6c0580

dnolen10:03:19

it’s a fun read

danielstockton11:03:33

Anyone have a snippet in cljs for detecting ios version?

mfikes13:03:15

@danielstockton Is your code running in Safari on iOS, or is it in a React Native app?

danielstockton13:03:48

Safari @mfikes. I've loosely translated from JavaScript in the end:

(def ios-version
  (if (.test #"iP(hone|od|ad)" js/navigator.platform)
    (let [[_ ma mi pa] (.match js/navigator.appVersion #"OS (\d+)_(\d+)_?(\d+)?")]
      [(js/parseInt ma) (js/parseInt mi) (js/parseInt (or pa 0))])))

mikerod15:03:15

I was reading through the cljs.analyzer ns some recently and I noticed there are 2 similar parse mutlimethod implementations to parse an ns form, one for ns and one for ns*. * https://github.com/clojure/clojurescript/blob/v1.10/src/main/clojure/cljs/analyzer.cljc#L2564 * https://github.com/clojure/clojurescript/blob/v1.10/src/main/clojure/cljs/analyzer.cljc#L2688 This implementation is rather lengthy and I was surprised to see it duplicated (or close?). Is one still around for historic reasons or are they both used and both have a reason to be maintained separately?

mfikes15:03:12

@mikerod If I understand correctly, the ns* variant was added to cater to the slightly different needs of top level (require ...) being in a ClojureScript file.

mikerod15:03:53

@mfikes interesting, I haven’t looked at them close enough to see the difference I guess. I was getting a bit confused doing a search earlier because I kept searching in the 2 different impl’s and the code all looked the same, but line numbers were diff (of course). hah

mfikes15:03:23

Perhaps a refactoring could share the common aspects.

mikerod15:03:20

yeah, it’d seem so

lwhorton17:03:24

Hi guys; I’m on a mission to convince some engineers to consider clojurescript as their front-end mainstay for complex apps. I’ve been using it for >2 years and have most certainly sipped the coolaid. I’m having a hard time looking back at the Triassic period and making reasonable, non-arrogant-sounding arguments against other solutions. Has anyone successfully convinced an org to consider the cljs ecosystem? If so, how? Could you link to any discussions, blogs, presentations, etc. that proved helpful? I’ve done about a week of digging and have found a few good pieces that really help prove my point, but the more the merrier.

JJ17:03:14

with es6/7/8 and the typescript stuff is a hard sell these days 😉

JJ17:03:56

if you are already using clojure in the backend I guess it will be a lot easier

lwhorton17:03:37

JS/ES6/ES20** and some framework like meteor/angular/ember/vue feels so archaic. If you move up to a react/mithril approach, you get slightly better with something like flux/redux, except now you also want an immutable js implementation, which in my own head is basically writing in a new language that kind of looks like js. perhaps the most valid counterpoint is something like typescript, but even here you’re only introducing types… which (without getting into a huge type debate) isn’t really solving the real problem in client-side js (immutability/state management).

JJ17:03:40

I think the primary users of cljs are clojure users (duh?)

JJ17:03:52

@lwhorton yeah, clojurescript is like immutablejs with lodash and much more but in a more consistent and idiomatic package

JJ17:03:36

that might be a pitch 😉, why use lodash and immutablejs when there is cljs?

lwhorton17:03:36

i’ve considered re-implementing the excellent re-frame library in js, which is a very well thought out flavor of redux-- minus the boilerplate, explosion of nouns, performance problems, and constant churn. but that comes with a whole lot of baggage as well… like the fact that you still need immutability in a language that doesn’t support immutability.

JJ17:03:47

lodash and immutablejs don't even play well together

lwhorton17:03:01

@devicesfor good point. at one point I was looking at modi, which is an exposed js API to cljs’s immutable structures… it more or less exactly matches the cljs api wrt conj assoc merge map/filter/reduce etc. But if you’re going so far as to commit 100% to an api that is non native JS, why not just use a proper transpiled language?

danielstockton17:03:33

My main arguments would be: 1) Google Closure - code splitting and dead code elimination are still ahead of webpack (don't know about typescript) 2) I get a lot done with just the core language (rich functional primitives, immutable data structures, core.async...). I'd have to pull in a load of dependencies in js land.

lwhorton17:03:26

it seems silly in the end to say: lets use babel, webpack, some es6 transpilation, es2017 future-feature transpilation, plugins for live reloading, tree-shaking, n-number of necessary libraries (promises, async/await, collection mutators), an immutability library, maybe a reactive programming plug like rxjs/cyclejs, and a cumbersome/heavy rendering framework, some boilerplate-ridden state management framework, etc. all of that just to get to “the state of usable javascript”

lwhorton17:03:23

where as @danielstockton mentions; lein, fig, google closure, an http client library, maybe re-frame, and everything else is native to the language.

danielstockton17:03:40

Exactly. lein new figwheel myproject and you're done with live reloading

lwhorton17:03:36

^ this is the problem I’m bumping into over and over when trying to argue against the latest and greatest in the JS world: none of it makes any sense compared to cljs (at least in my head).

JJ17:03:42

cljs is also more concise though, less lines of code

JJ17:03:17

@lwhorton performance might be an issue, for ex: code size

JJ17:03:38

well not an issue, something they can use against you

lwhorton17:03:13

performance from a kb-down-the-wire standpoint?

JJ17:03:35

yes, and execution

JJ17:03:59

the other issue might interop, writing extern files

lwhorton17:03:11

I wonder- does anyone have a real-world example where the cljs runtime is meaningfully harmful to performance?

JJ17:03:20

extern inference is not mature enought yet I hear

lwhorton17:03:44

in my experience, when you’re in the browser the only place performance matters is touching the dom while rendering… everything else is an order of magnitude faster

JJ17:03:44

if you need external libs that is

danielstockton17:03:49

I think the code size argument only works for non-complex, tiny apps. Code size is likely smaller for complex apps, where you can take advantage of dead code elimination and minimal dependencies.

JJ17:03:09

@lwhorton on mobile, network and size matters

danielstockton17:03:01

I also consider om/fulcro/qlkit to be state of the art frameworks for complex apps. I wouldn't know how to easily recreate that in js. Graphql/relay isn't really it.

JJ17:03:05

how important that is to your app its another matter

lwhorton17:03:53

I agree with @danielstockton wrt code size: jquery alone is 29k gzipped, and a minimal cljs app is 23k.. but once you start adding libraries cljs scales much better than linearly, while a js impl scales at-least linearly.

lwhorton17:03:51

for example, immutable js is 16k… so just to have a bare-bones usable environment with native JS + immutable, you’re only 7k more than the fully feature cljs language

JJ17:03:45

@lwhorton ok, what about using third party libs?

lwhorton17:03:05

i think the biggest pain point is external integration with js libs.

JJ17:03:33

you have to be ready to maintain extern files, ex: keep them update with upstream

lwhorton17:03:47

that being said, you can completely avoid the issue of externs or cljsjs by using this macro library… (looking for it on gh)

justinlee17:03:59

If you want to convince a javascript team to use clojurescript, you really should set up a shadow-cljs project and forget about cljsjs and externs and all that.

justinlee17:03:17

@lwhorton I think you are thinking of cljs-oops

lwhorton17:03:22

i only cursorily looked into shadow-cljs, given my preference to boot/lein. do you have experience with it?

justinlee17:03:07

It’s what I use. I don’t like lein because it is too magic and when something goes wrong it is hard to fix. shadow-cljs is more explicit. But if you need to use a lein plugin--and you probably will--shadow-cljs plays nice with lein too. The biggest thing for me is that shadow-cljs lets you resolve npm modules exactly like you would if you were writing javascript, including modular libraries like material-ui that allow you to include parts of the library independently. It also has a bunch of fixes to externs inference and just seems to work most of the time. https://shadow-cljs.github.io/docs/UsersGuide.html#js-deps

justinlee17:03:58

I’ve seen reports of teams switching to shadow and seeing significantly smaller code size, though it wasn’t clear why.

lwhorton17:03:24

thanks for this. i’ll have to give it a more detailed investigation.

justinlee17:03:49

be sure to hop into #shadow-cljs if you have issues

justinlee17:03:32

or questions for that matter. @U05224H0W can probably do a more comprehensive job of explaining all the optimizations he’s put in and he’s very interested and responsive in understanding how and why people use it. my biggest point is that shadow feels much more sane and much lower friction for the javascript->clojurescript transition

lwhorton17:03:01

have you used boot at all? how does it compare in your opinion?

justinlee17:03:50

When I first started, I used boot. I stopped because people were basically like, “use figwheel” and it was easier to get going with lein. Boot is a little confusing because it’s not only a replacement for lein, but it is also a replacement for certain lein plugins. I didn’t really give it a fair shot. I highly suspect that if you have complex builds and need to do a bunch of custom stuff, boot is more robust and easier to understand.

lwhorton18:03:07

i used boot for quite some time and I think ultimately they missed the mark

lwhorton18:03:20

they wrote an immutable abstraction layer on top of a mutable file system, which in practice is great for a single self contained jvm… but in reality there is a very high cognitive overhead. while you are allowed to “write your build system as a program, not a config file”, doing so is a struggle

lwhorton18:03:26

if i haven’t touched the build process for a couple weeks, then need to go make a change, it takes me a good while to jump back into how the whole immutable system works

justinlee18:03:48

what was wrong with makefiles anyway 🙂

lwhorton18:03:19

from a client-side perspective, the whole ecosystem is what’s wrong with old build systems. transpile *-> javascript, transpile style->css, template->html, assets->optimized

lwhorton18:03:34

I do still use make as essentially a CLI tool, though

JJ17:03:22

externs is the most mature way

JJ17:03:32

manual externs

justinlee17:03:12

externs is going to scare the crap out of javascript people. “I mean have to mess with this janky poorly documented header file to import a js library?”

JJ17:03:34

I wonder why no one has created something to convert typescript externs to for clojurescript, they have a big active community

Josh Horwitz17:03:59

Is anyone using Fulcro at all? The documentation is one of the best I have ever seen, but I don't know much more than that

lwhorton17:03:35

I looked at fulcro from a distance a little while ago. the problem for me is that I don’t want a full-stack front client and server solution

lwhorton17:03:14

i think it’s much more reasonable to have a consistent cljs client from project to project, but server side requirements change too frequently for me to buy into a whole ecosystem like that.

Josh Horwitz17:03:21

I can see that, I wasn't aware it was full stack

lwhorton17:03:19

yea.. for example if I wanted to write a fault tolerant distributed system in elixir… i’m no longer in the safe-confines of fulcro but have to come up with two new systems to use

Josh Horwitz17:03:43

what would you use for the frontend?

lwhorton17:03:26

if I were a dictator it would be cljs + re-frame + reagent

lwhorton17:03:27

by far the most well-thought out front end “framework”: extremely performant, declarative via data, extensible, middleware layer, single source of truth, easily coordinate complex workflows

Josh Horwitz22:03:01

I couldn't find any good walkthrough tutorials for reframe

lwhorton23:03:04

honestly the best resource is the README, and if you use github’s history I think the original readme’s were even better (but more opinionated and “sassy”, so they changed them)

lwhorton23:03:39

but eric normand has a whole series on learning it: https://purelyfunctional.tv/guide/re-frame-building-blocks/ (paid and unpaid stuff in there)

Josh Horwitz01:03:00

Great, thanks alot

JJ17:03:54

like a way to get leverage all those ts externs, that way cljs would get all that work for free

danielstockton17:03:24

I've created quite a few cljsjs packages, and i found it pretty easy. That being said, there are only a handful of libs i use regularly (charts, google maps). I find it doesn't take too long to write my own libraries for other things (e.g. datepickers) in cljs, and I end up with something better than combining 100 badly written js libraries and trying to customize them to my needs.

lwhorton17:03:34

having worked on a 2+ year project in cljs with quite a few cljsjs dependencies… it just stinks all around

rauh17:03:07

Just use shadow-cljs, you can use any NPM dep with it, no externs necessary. And thheller is super responsive

lwhorton17:03:07

^ exactly that @danielstockton… give me a day to rewrite some half-working js library in cljs, and in the end it will save time and integration pain

justinlee17:03:31

There’s just no reason to use them. With shadow-cljs you just install stuff with npm and maintain a package.json. Externs inference works well or you can use cljs-oops.

rauh17:03:04

oh, just saw you already mentioned shadow-cljs

justinlee17:03:34

Great minds. 🙂

justinlee17:03:08

I feel like shadow-cljs doesn’t get enough credit so I’m happy when other people recommend it.

orestis17:03:33

FWIW, I moved to a new position and talked about CLJS, Dead Code Elimination, sane tooling etc. to my CTO — but before that, I shared a few Rich’s talks, and he caught the bug from those — he justs wants a system that is easy to reason about, and the current Node codebase is a huge ball of mud. So if we end up picking up CLJS, it is going to be for the systemic effects it has, rather than the syntax/libraries etc.

lwhorton17:03:08

^ which talks did you show her? most definitely “simple made easy”, but was there anything else in particular that got you buy-in?

orestis17:03:40

Simple made easy — and I think he went on a binge 🙂

orestis17:03:17

He also finds Lisp beautiful, which is not a common think — we will have to do a lot of pursuading within the team about this 🙂

orestis17:03:51

It’s hard to describe how messy can a “simple” web app written in Node/Express/Mongo can be.

orestis17:03:03

(I went to this company partly to help them untangle the mess)

lwhorton17:03:47

i’m in a similar situation-- not a product company but a consulting service that is looking for help standardizing on an effective, useful front-end stack

orestis17:03:27

I’m mostly talking about back-end so far — which is quite easier to sell as you have a lot of ways to gradually convert, whereas frontend will have to be a major rewrite.

lwhorton17:03:51

true.. client side apps are quite painful to split and bridge because they’re essentially one system not separated by network boundaries or queues

danielstockton17:03:29

A problem I've seen against adoption is getting people to change their editing habits. They instinctively want to open a file in their usual editor and start editing in their normal way. It can really pay off to introduce the advantages of structural editing, CIDER, etc..

danielstockton17:03:58

When they're inserting parens by hand and spacing to indent things, it's harder to get them to see the real benefits.

rauh17:03:16

100% ACK. I've been wanting to do an intro video about this... With Cursive

orestis17:03:43

Wait, I’m inserting parens by hand, and correct indentation by spaces a lot of times — I don’t find it that cumbersome…

lwhorton17:03:25

that’s another good point, i’ll add it to my list

danielstockton17:03:10

I've seen people really struggle with the editing, and I'm watching them get frustrated before I've had chance to show them anything.

JJ17:03:52

ok, so the biggest blocker is interop

danielstockton17:03:11

Perhaps that's an overstatement, but I've definitely had cases where I can totally see why they don't consider the syntax an advantage, rather than just different - and i think it is.

danielstockton17:03:04

To convince people to learn an unfamiliar sytax, convincing them of the advantages of said syntax can be important.

lwhorton17:03:13

i agree. I think “its homoiconic, how cool!” is not a really compelling argument.

lwhorton17:03:40

and the intricacies of lisp-1 vs lisp-2, macros, structural editing, etc. are really only things you appreciate after quite some familiarity.

danielstockton17:03:40

I think those are the two main fears: 1) less widespread adoption (hiring, help, libraries) and 2) weird syntax and editing experience

justinlee17:03:36

In my opinion, using words like homoiconic and talking about macros misses the point. The uniform syntax of lisp means that you can always refactor code in a way that is not really possible in a language like javascript.

danielstockton17:03:37

I don't have a really convincing argument to 1), other than that I think most libraries are overated and clojure hires are smarter 😛

danielstockton17:03:41

It's easy to dismiss though

justinlee17:03:30

For me, that’s why I like clojurescript. When you start to have hundreds of lines of ugly repetitive crap, you know that you can fix it in any lisp-based langauge.

Jacob Haag17:03:52

Hello everyone, quick question, does anybody have any recommendation as to how to use the 'fs' phantomjs module in a cljsfile? I have been looking into the :foreign-libs option in cljsbuild but to no avail

JJ17:03:28

^^ example 😉

lwhorton17:03:34

perfect timing

orestis17:03:49

Meta: perhaps there should be an “evangelism” channel? I’m sure a lot of people would like to bounce around ideas on this…

orestis17:03:09

Who must we ping? @seancorfield?

seancorfield17:03:56

@orestis You rang, sir? 🙂

orestis17:03:07

appears in a puff of smoke 🙂

seancorfield17:03:20

We have #community-development which is probably appropriate for that.

seancorfield17:03:51

(it was originally created to discuss what we should all do in the case of the Slack shutting down large communities but it really is broader than that)

orestis17:03:55

OK, your call — I’d think that #evangelism would be a more apt title for this discussion FWIW 🙂 It might help with discoverability a bit more.

orestis17:03:26

As in, you might want to sell Clojure to your company but it will take some time until your coworkers become a proper member of the community.

seancorfield17:03:38

The "channel details" for #community-development say "community growth & support" and evangelism fits into that...

JJ18:03:26

it will get delete anyway 😛

dnolen18:03:17

One interesting datapoint is that I believe that the second most popular language that people are coming from is JavaScript

dnolen18:03:28

Even a couple of years ago that wasn’t close to being true

dnolen18:03:40

So I think the JS fatigue effect is quite real

gnzlrm18:03:49

Which is the first one? Just curious.

gnzlrm18:03:11

Oh, cool. Then I'm part of the stereotype, makes sense.

dnolen18:03:34

I’ve long believed that we don’t need to cater to JS beyond exposing the ecosystem

dnolen18:03:51

And I believe that’s a big value proposition given how complex JS is now

JJ18:03:36

I thought the first one would be clojure

dnolen18:03:12

Clojure people use ClojureScript it’s not an interesting datapoint

dnolen18:03:27

Hasn’t been for a few years now

zentrope18:03:42

If including jQuery + a JS file in your page was 1x, using CLJS was 2x. But these days, even with helpers like create-react-app, yarn, etc, using JS is about 4x, while CLJS has remained at 2x.

zentrope18:03:49

All those JS state-management strategies are reduced to just using an atom (for example).

zentrope18:03:07

And immutable by default erases all that confusion between normal JS and Immutable JS (etc).

dnolen18:03:07

I also think the modular thing only sounds good in theory

dnolen18:03:31

For most users, monolith a la Closure is just less hassle

zentrope18:03:38

Core.async erases all that promise complexity.

justinlee18:03:36

except that core.async also eats your exceptions… it’s almost great 🙂

lwhorton18:03:16

check out fullcontact/full.async

justinlee18:03:21

Yea. Not helpful unless libraries use it unfortunately.

justinlee18:03:27

But yes, that’s what I’m talking about.

zentrope18:03:45

With CLJS, you learn about four things, and then you watch the JS world re-invent them every other month.

JJ18:03:57

@dnolen what modular thing? code splitting?

JJ18:03:21

is core.async with a react wrapper even a good idea?

dnolen18:03:30

No I mean JS unhealthy obsession with modularity / pluggabilty

dnolen18:03:40

It doesn’t actually add up to gain

dnolen18:03:44

Just a lot of pain

zentrope18:03:45

@lee.justin.m Maybe so, but my use is so lightweight I just don’t have the issue anymore.

zentrope18:03:38

@devicesfor I use it as an event loop. Components take a channel, send events on it. The events are processed, returning a new state.

zentrope18:03:00

@devicesfor No “wrapping” if I understand you correctly.

JJ18:03:53

I mean using both core.async and something like reagent at the same time

zentrope18:03:26

Oh. I don’t know. I use a single atom for state and a multi-method to process events. Rum (and Quiescent before that).

zentrope18:03:01

A long time ago I didn’t like the fact that you could have State all over the place in reagent and never looked back. I’m sure I don’t have a fair view of it.

justinlee18:03:32

maybe you mean something else, but it is really nice to be able to download high quality react libraries and have them work. given that there are about 6 flavors of javascript in the wild, it is kind of incredible that it does work. i’d say that’s a big gain

zentrope18:03:33

Regardless, for me, anyway, the more I can get away with just the base language, the better. (Well, + core.async.)

mccraigmccraig18:03:04

what promise complexity were you talking about @zentrope? callback-hell?

mccraigmccraig18:03:47

we're erasing that with a promise-monad which works out very nicely... and is another argument in support of a lisp - the "do" transformation used to give a clean syntax is a macro

richiardiandrea18:03:40

I think I know what he means, promise rejections do not compose well because they are not data. IMHO that is the main reason for using channels for async (sorry if I misunderstood, just wanted to vent this out 😄)

zentrope18:03:54

@mccraigmccraig I’ve not spent enough time with them, but I’ve used “fetch”, say, and it’s hard to just get a value out of it in a synchronous way. Hard to debug.

dnolen18:03:23

@lee.justin.m pluggability is different from writing a la carte useful things

dnolen18:03:48

React just demonstrates that functional programming is a good idea - but we already know that

zentrope18:03:01

@mccraigmccraig I also get the impression that promises are just an attempt to paste-over the need for actual threads. do.this().then().then().then().

mccraigmccraig18:03:14

@zentrope if you were using a thread for that then you would be blocking, which would be rather a waste

mccraigmccraig18:03:50

if you are actually using the .then() methods on promises though then that's certainly not a very nice way to program with them

justinlee18:03:57

promises are an attempt to paste over the need for callbacks. callbacks are an attempt to paste over the need for threads

zentrope18:03:51

Weren’t threads invented to solve the issues related to kpoll eventing style stuff?

zentrope18:03:08

The biggest problem I’ve seen in threads aren’t blocking, but shared-state.

zentrope18:03:24

Not every app is a 10k web server.

zentrope18:03:46

¯\(ツ)

mccraigmccraig18:03:00

well, it's true promises paste over callbacks, but they also give you a value to pass around, which is very powerful - it enables promise composition

mccraigmccraig18:03:25

and once you realise that promises form a monad then you are laughing

zentrope18:03:41

All that aside, a pattern I’ve noticed over the years, esp. in Java Culture, is that practically every new thing is an antidote to the previous poison: but it’s rare that folks think about maybe not taking poison in the first place.

mccraigmccraig18:03:59

you can have your async code, with error handling, short-circuit evaluation and composition in a sync-link syntax

zentrope18:03:29

@mccraigmccraig It might be I’ve just never had the need for it in my super-simple get-data-put-in-database apps.

mccraigmccraig18:03:17

on the backend that's true (although my current app is all async on the backend too and it's working out very nicely)... but in js you are forced to do everything async, so it's worth figuring out reasonable ways to deal with it

mccraigmccraig18:03:39

clojure and clojurescript have the tools for it though

justinlee18:03:03

This may give you a hints as to why: https://brendaneich.com/2007/02/threads-suck/ It sure is nice never to have to think about concurrency in javascript.

mccraigmccraig18:03:33

have a look at this for an example of a nice way of programming with promises in clojure/script http://funcool.github.io/cats/latest/#manifold-deferred

justinlee18:03:52

I’d love to try to use that library but man the docs are dense. I don’t give a s** about category theory and I suspect most working programmers don’t either.

zentrope18:03:14

That article confirms that it’s shared, mutable state that’s the issue, not threads, no?

justinlee18:03:12

that article may provide insight as to why eich kept it simple

zentrope18:03:31

Oh, I don’t challenge his choices.

zentrope18:03:56

But I do think there’s a bit of religion about “async all the things” that is kinda fundamentalist.

justinlee18:03:51

maybe i miss the point: browsers implement the model and it’s not going to change anytime soon so how do i write code today to deal with that

zentrope18:03:53

Wouldn’t it be cool if you could just write your code with one step after another, then let the runtime figure out how to execute it in chunks?

justinlee18:03:26

people have been working on that research project longer than i’ve been alive

zentrope18:03:27

Ah, I get you.

zentrope18:03:41

So, to summarize, for the browser apps I write, just about every action (coming from the server or initiated by the user) results in an update to “State”, which then causes a re-render. With core.async, there is no callback issue (all callbacks just drop an event in a channel), so the need for promises isn’t there for me. Thus, to me, promises seem complex in the amount of ceremony.

zentrope18:03:49

For error handling:

zentrope18:03:40

(when-let [event (<! ch)] (try (process event) (catch :default e (process {:event :error :e e})))

zentrope18:03:21

Throw in some more data in the error case (such as the original message), and … praps I’m naive. :)

mccraigmccraig20:03:05

i'm an "async all the blocking things" fundamentalist @zentrope 🙉

zentrope20:03:44

Heh. Well, enjoy!

justinlee18:03:27

@dnolen Ah ok. I don’t just quite get what you mean by pluggability.

justinlee18:03:47

@richiardiandrea That’s interesting: I’d love to understand what you mean. The one thing I miss about javascript is a language-level promises because they have a clearly defined error channel and work as expected with uncaught exceptions.

justinlee18:03:40

then-chaining can be annoying but async/await is the same syntax as go blocks, but with the caveat that exception have better behavior in js

richiardiandrea18:03:25

I personally prefer to compose data and identify errors with functions, like passing back error maps like:

{:error {:msg "message"}}
It gives me more flexibility and I am not tied to a mechanism that may or may not suit me. Also promises can pass back any data for errors but it is not considered good practice. Having said it is always case by case and I try to understand first if it's worth adding the channel complication or not to my code path. Sometimes it does sometimes I use promises. It depends.

justinlee18:03:05

Okay I think I understand your point. I think mine is slightly different: I’m coming at this from a debugging and tooling perspective. If you or a library writer screw something up and fail to catch an exception or miss an error condition, you are likely at least get something with promises. Not necessarily so with channels, which require careful error handling on the “put” side and an ad hoc convention (e.g. dnolan’s <? operator) for error channels.

zentrope18:03:46

Maybe libraries shouldn’t use channels?

justinlee18:03:07

or maybe the go macro could catch exceptions and the <? semantics are the default take behavior. i know this isn’t going to happen but it seems to me that it would make channels behave more congruently with exceptions

zentrope18:03:53

Hm. I see that you can create a channel with an exception handler.

richiardiandrea18:03:07

My point is exactly the opposite of that, I rarely want to throw exceptions except at the very boundaries

richiardiandrea18:03:20

so I try to avoid Promises as much as possible in the "business logic" part, if complicated enough to justify channels

justinlee18:03:52

“want” isn’t the point. if you fail to realize that, say, json.parse throws an exception, the resulting behavior is bad. see e.g. https://github.com/r0man/cljs-http/issues/110

justinlee18:03:18

(and sorry if that sounds aggressive or pokey. i didn’t mean it that way)

justinlee18:03:26

and to be clear: I LOVE channels, I’m just annoyed that the way to use them seems to be “just always write correct code or else you’ll get a severed stacktrace in the middle of a blizzard of gensyms”. i want them to be great and also easy for dumb programmers like me to use

escherize19:03:04

I’m bolting a cljs project onto a javascript codebase. Can I setup the cljs compiler to access the npm dependencies in the javascript project? would symlinking their package.json and copying the deps i need (and versions ) into the :npm-deps key be a good way to go?

richiardiandrea19:03:10

@escherize if I understand the problem correctly, you'd just need to duplicate the :npm-deps from the ones in package.json and add :install-deps false

justinlee19:03:27

@escherize my opinion is that shadow-cljs will be better for this. it already uses the package.json to do npm resolution

escherize19:03:34

@richiardiandrea thanks for a quick response — but will those deps be loaded twice? (the js app loads the cljs code from the index.html, so i’m afraid would they both package their own (identical) deps?

richiardiandrea19:03:57

@escherize isn't require idempotent?

escherize19:03:59

@lee.justin.m thanks for responding too! I’ll take a closer look at shadow-cljs.

richiardiandrea19:03:31

sorry for answering with a question 😄 I think you should not have that problem

justinlee19:03:08

@escherize I think the way to do what you want is to use the :npm-module target with shadow and then just use it as a library. https://shadow-cljs.github.io/docs/UsersGuide.html#target-npm-module You should hop into #shadow-cljs if you have issues.

Garrett Hopper19:03:17

Does it make sense to use code-splits with html5 async script tags? I'm not sure if it provides any benefit, and it doesn't work without :optimizations :advanced, since document.write can't be used in async scripts.

thheller19:03:08

:advanced doesn't use document.write, only :none does.

Garrett Hopper19:03:48

Yeah, it doesn't work wirhout :advanced.

Garrett Hopper19:03:59

Which is fine. I suppose I could have separate dev and prod index pages.

thheller19:03:50

shadow-cljs has an option for makes <script async ..> work in :none but default CLJS does not

Garrett Hopper19:03:05

I'll have to look into that. Thanks

thheller20:03:14

:async-require wasn't properly documented but I just added a section in case you are interested. https://shadow-cljs.github.io/docs/UsersGuide.html#_async_loading

wilkerlucio21:03:46

@lee.justin.m I have a project I used a lot of core-async, I agree the vanila things miss important stuff (important in the sense that I see to always require then to use it effectively, but I do it mostly on cljs, so maybe it's something about that). well, adding the <? is very good, but not enough, another one that seals the deal is the (go-catch), that's a go block that will re-throw exceptions, this way you can work very much like in a promises world

wilkerlucio21:03:41

so, had you tried using that?