Fork me on GitHub
#clojurescript
<
2019-10-31
>
tekacs00:10:43

I’m curious if any other folks have ended up writing a ‘hook’ as a macro… (it’s not gratuitous, load-css! uses shadow.resource/inline which requires a literal string argument, otherwise I’d find this very surprising)

danielneal10:10:12

makes sense, took me by suprise at first but pretty clever

danielneal10:10:32

Heya, I’ve got a question about transit and cljs - how to reduce encoded size while keeping the ui repsonsive https://ask.clojure.org/index.php/8795/how-can-reduce-the-size-transit-encoded-data-clojurescript if anyone has ideas, would be much appreciated!

Roman Liutikov11:10:11

What's that 6mb limit in Android? Is it async storage?

danielneal11:10:54

yeah. Apparently you can increase it via gradle properties, but I’m using expo and don’t want to eject at the moment

Roman Liutikov11:10:35

Would be an option for you to use sqlite instead? Of whatever dB there's on Android

jrychter11:10:49

This is only tangentially related, but I've encountered what I think is a bug in nginx, where JSON proxied from my app gets truncated at 3MB.

scknkkrer11:10:06

Any D3 Wrapper or at least React-ifyed Component for D3 ?

scknkkrer12:10:26

Amazing library, amazing work!

👍 4
dominicm12:10:06

I'm so excited by it

orestis13:10:32

We used vega-lite with good results, but it's a lot of concepts to understand.

👍 4
erik13:10:05

could vx be made to work with https://github.com/Niekes/d3-3d or equiv?

dominicm13:10:49

Vx/d3 is on a different level to vega. Sometimes you've got very specific designs for a graph, and d3 is the better tool

yuhan14:10:15

There's also this: https://github.com/gadfly361/rid3 which I've only played with briefly

Eric Ihli15:10:03

I'm new to Clojure[Script] and I can't figure out this error message. I started a new project using `lein new chestnut calculator -- --vanilla https://github.com/plexus/chestnut I added cljsjs/react as a requirement and updated my core.cljs file. Here is the diff

diff --git a/project.clj b/project.clj
index 0b1f23a..df9dac9 100644
--- a/project.clj
+++ b/project.clj
@@ -16,7 +16,9 @@
                  [com.stuartsierra/component "0.3.2"]
                  [org.danielsz/system "0.4.1"]
                  [org.clojure/tools.namespace "0.2.11"]
-                 [compojure "1.6.1"]]
+                 [compojure "1.6.1"]
+                 [cljsjs/react "15.1.0-0"]
+                 [cljsjs/react-dom "15.1.0-0"]]
 
   :plugins [[lein-cljsbuild "1.1.7"]
             [lein-environ "1.1.0"]]
diff --git a/src/cljs/calculator1/core.cljs b/src/cljs/calculator1/core.cljs
index a2ce140..ec4ac84 100644
--- a/src/cljs/calculator1/core.cljs
+++ b/src/cljs/calculator1/core.cljs
@@ -1,4 +1,6 @@
-(ns calculator1.core)
+(ns calculator1.core
+  (:require [cljsjs/react]
+            [cljsjs/react-dom]))
 
 (enable-console-print!)
 
And when I run lein figwheel, I get the following error.
Failed to compile build :app from ["src/cljs" "src/cljc" "dev"] in 0.641 seconds.
----  Could not Analyze  src/cljs/calculator1/core.cljs  ----

  No such namespace: cljsjs/react, could not locate cljsjs_SLASH_react.cljs, cljsjs_SLASH_react.cljc, or JavaScript source providing "react" (Please check that namespaces with dashes use underscores in the ClojureScript file name)
I have confirmed cljsjs/react is in ~/.m2/repository/. So it's getting picked up as a dependency. Where can I find documentation around how lein/figwheel search for JavaScript dependencies while compiling?

herald15:10:40

Did you rename calculator1.core to calculator.core without updating the folder name?

Eric Ihli15:10:45

Sorry, that was a copy/paste error. I created the calculator1 project and then copied over the smallest amount of code to reproduce the issue. The error is not related to that. I corrected the name and get the same issue.

acron15:10:07

(:require [cljsjs/react]
               [cljsjs/react-dom]))

acron15:10:17

should be cljsjs.react

thanks3 4
acron15:10:23

and cljsjs.react-dom

acron15:10:28

(no slashes)

Eric Ihli15:10:11

Son of a.... that's even what the damn error message says. I had it in my head that the error message was about directory paths and not the require statement.

bcaccinolo16:10:59

Hi, is there a way to use async and await with Clojurescript ?

dnolen16:10:56

@benoit.caccinolo no but it's really not needed

dnolen16:10:15

it's pretty trivial to do exactly the same thing w/ core.async

bcaccinolo16:10:02

ok thanks, I’ll have to check core.async so 🙂

dnolen16:10:40

you can write a little helper macro like <p! over core.async <! that throws on Promise reject

dnolen16:10:57

then you get exactly async/await behavior

dnolen16:10:51

we'll likely write a little blog post about this in the near future

dnolen16:10:00

as this comes up over and over again and the solution is trivial

bcaccinolo16:10:27

thx I’ll check this

lilactown16:10:33

if you’re not actively using core.async, a lighter-weight solution might be to use a smaller library that deals just with JS promises, like kitchen-async: https://github.com/athos/kitchen-async

dnolen16:10:53

again I don't know what "lighter-weight" could possible mean

dnolen16:10:02

core.async isn't that much code

dnolen16:10:13

so people should stop saying this type of thing

dnolen16:10:38

if you use ES7 async/await

bcaccinolo16:10:46

that’s what I’m using rigth now but the nestings with p/then is annoying

dnolen16:10:53

and you need to target a wide variety of browser targets

dnolen16:10:04

the output is gonna be more or less what core.async does

dnolen16:10:08

generated state machines

lilactown16:10:38

you’re right, that’s not a good phrase to use. “If you don’t need/want the abstraction cost that core.async brings,” is what I meant in this case

dnolen16:10:49

again I don't know what means

lilactown16:10:02

sorry dnolen, I’m not trying to be obtuse. I think there is a jump from “use promises but with async/await” and “introduce channels” which are fundamentally different constructs. at some point a person using core.async in a code base will need to not only know promises, but also channels/CSP and core.async’s implementation of them.

lilactown16:10:50

there is a vague notion of performance that thheller is talking about but that’s lower on my feelings about this than the above

dnolen16:10:02

if you want async/await there is not difference in the cost

dnolen16:10:16

core.async is based on C# async/await

dnolen16:10:23

which is more or less JS async/await

bcaccinolo16:10:23

I would like to be able to write this const matches = await page.evaluate(utils.extractMatchesDataFromPage);

bcaccinolo16:10:30

no nesting 🙂

dnolen16:10:42

this is what core.async does

dnolen16:10:49

just like async/await

thheller16:10:20

> if you want async/await there is not difference in the cost

thheller16:10:24

that is just flat out false

thheller16:10:31

there is way more overhead involved in core.async

dnolen16:10:26

I've never been in scenario a where I could detect any type of meaningful overhead in runtime performance

dnolen16:10:48

nearly all real usage is I/O bound in jS

thheller16:10:13

async/await also has way better tool support ... eg. stacktraces

thheller16:10:43

but besides that core.async also generates a whole lot of code in some situations

dnolen16:10:00

sure I agree with that

thheller16:10:07

I do agree with you that core.async is "just as good"

thheller16:10:17

but saying that there is no difference is wrong

dnolen16:10:57

that's possible but far as I know when I look at the code gen for JS async/await it didn't look meaningfully different to me

dnolen16:10:04

there's not that much innovation in this space

dnolen16:10:08

you have to make a state machine

thheller16:10:22

well the whole point is that there is no code gen in modern browsers

dnolen16:10:38

you have to target N browsers

dnolen16:10:41

or JS targets

dnolen16:10:45

I already said this above

thheller16:10:11

but core.async still generates a lot more code than the equiv transpiled async/await stuff

dnolen16:10:31

yes if you know you're targeting something w/ native async/await of course

thheller16:10:00

a whole lot of use cases can use native async/await .. that is the point. you can't just look at the browser anymore

thheller16:10:21

electron, node, react-native (maybe, not sure about that one)

dnolen16:10:27

to be honest I haven't looked at this in a while - but when I compared this back when we developed core.async and optimized it

dnolen16:10:47

the differences didn't seem meaningful to me, 2X maybe - then factor in advanced compilation etc.

thheller16:10:16

well it is different nowadays ... async/await beats out hand-written promise code in pretty much all cases

lilactown16:10:10

I said this in a thread above… I also think that there is a greater cost to adding core.async to a codebase than performance (if that’s the case). adding core.async to a code base requires one to now think in channels, and understand core.async and it’s machinery, as well as how to interop with promises. it’s just more stuff, vs. promises with async/await syntax.

dnolen16:10:16

I don't know the last time I worked on a project where the code size was coming from core.aysnc usage

dnolen16:10:43

so this is all splitting hairs in my opinion

thheller16:10:07

I can show you code right now in the core.async library itself that generates massive amounts of code

thheller16:10:48

but it isn't so much about the code size alone. chrome devtools support is just better for promises

thheller16:10:55

stacktraces in core.async are still bad

dnolen16:10:03

currently we're using core.async in multiple situations for exactly what was being discussed

thheller16:10:09

I do use core.async myself all the time ... I mostly agree with you. just saying that there is "no difference" is what bothered me. there definitely is a difference nowadays

dnolen16:10:21

it works well enough and in the typical HTTP fetch I/O situation debugging isn't that interesting

dnolen17:10:57

all I meant what that if you can't depend on the presence of async/await then you're going to have to codegen

dnolen17:10:01

it doesn't matter what you use

dnolen17:10:28

it maybe the case that that core.async code gen is 2-4X worse than current JS tooling

dnolen17:10:40

but if you're writing reasonable code I don't see how this is going to matter

dnolen17:10:46

to me it's just as misleading to state that core.async is somehow not pretty well suited for dealing w/ JS promises

dnolen17:10:53

it works great in my experience

thheller17:10:47

it works great if you know how to build the <p! and how to turn a core.async channel into a promise

dnolen17:10:58

(which isn't to say it doesn't need working on or improvements)

thheller17:10:03

that part is deinitely missing from the lib)

dnolen17:10:08

yes I already said I was going to write a post about that 🙂

lilactown17:10:50

yeah I’m thinking about this from the perspective of someone who hasn’t used core.async before

lilactown17:10:00

and is looking for async/await like they would expect in JS

didibus17:10:22

I mean, that's true of someone who hasn't used async/await as well. There's a learning curve to both models

lilactown17:10:25

telling someone to “add core.async and channels” is like… okay now I have 2 problems lol

dnolen17:10:33

the above is really all you need (or something similar)

lilactown17:10:36

async/await is quite a bit more ubiquitous

lilactown17:10:51

and documentation for JS async/await is quite prevalent

dnolen17:10:02

I don't think anybody would be against getting those into cljs.core.async

didibus17:10:17

I'm also not sure the performance of core.async is worse than native async/await. Async/await beats hand written promises. But core.async isn't hand written promises.

didibus17:10:10

I'm personally interested in learning why promesa got rid of async/await.

didibus17:10:29

Maybe async/await isn't the best for for Clojure?

Filipe Silva17:10:30

I was looking into the async/await case a week or so ago. Mostly I wanted to use a third party lib that gave me promises. I tried hard to figure out what the "easy" way of using core.async was, and I could not find any good example. I ended up using Promesa for a while. Then I removed Promesa and just used threaded (.then ...). I really would have loved to use core.async in a idiomatic clojure way but the information just wasn't available. And since I was making code that novices would have to understand, that was a deal breaker.

lilactown17:10:45

IME the times I’ve needed channels while developing modern front-end apps has been pretty small when using React. most of the places that we introduced core.async in the past, I’ve later gone back and refactored to use promises and it was an improvement. that could have been our misunderstanding of how and when to leverage core.async in the past, but that’s kind of my point.

lilactown17:10:26

promesa’s async/await had some bugs and they may have weighed the cost of supporting it and decided to drop it

Filipe Silva17:10:46

IMHO is core.async is a great way to interact with promises there really needs to be a official guide and example for it.

Filipe Silva17:10:55

you just cannot avoid promises when using libs

didibus17:10:16

Their commit message said something along they didn't find it useful given the added complexity of maintaining it.

dnolen17:10:56

w/ the above macro+helper you can literally write exactly what JS async/await examples show

dnolen17:10:16

I was able to copy nearly verbatim Puppeteer async/await examples way

dnolen17:10:39

arguments about core.async vs. promises just don't make any sense to me

dnolen17:10:45

it's not a vs. thing at all

Filipe Silva17:10:06

I don't question it's possible. I assert that, if I want to show a novice how to use core.async with promises, I cannot give them a good resource.

thheller17:10:13

FWIW it would look like this

thheller17:10:30

just the await macro is missing from core.async 😉

Filipe Silva17:10:48

does that exit? promise/await?

thheller17:10:50

david posted the <p!. that basically.

Filipe Silva17:10:24

It might sound weird but I don't understand what that does, nor how to use it. If it's the right thing to use, it's very daunting. I couldn't come up with it for a while after learning cljs.

Filipe Silva17:10:30

The https://github.com/athos/kitchen-async puppeteer example is great and makes sense. I can show that to someone and they know how to use it. If there was a similar example for core.async in http://clojurescript.org/ that would be a nobrainer.

dnolen17:10:38

this is such a regular reoccuring topic that I agree we need a post + maybe just standardizing into cljs.core.async

lilactown17:10:50

and I assert that, telling someone that wants to deal with promises easier to introduce core.async into their app is not the best idea. It’s easy for us to tell other people to do that because we already paid the cost to learn CSP and core.async. in general, I don’t want it in my code now. But i’m also happy with threading .then to avoid it

👍 4
Filipe Silva17:10:46

you can turn that argument the other way around and it goes something like "you can learn start using core.async for promises and understand it better later".

dnolen17:10:02

I think stating a standard way to solve a problem doesn't mean you can't ignore it and do it your own way

dnolen17:10:31

but this a recurring beginner question

dnolen17:10:52

I think it's more effective to say do it this way - then send them off to make a bunch of decisions

dnolen17:10:14

they can always make different decisions later once they know at least one way to do it that reasonably straighforward

didibus17:10:49

I have to admit not doing a lot of front end. So I'm curious, is async/await really nicer then threaded .then ? I feel core.async is at it's most useful when the amount of concurrency is high. But my impression is most situation for async/await are very simple, like, I wish this operation was synchronous, I just want to do a then b then c.

Filipe Silva17:10:33

personally I don't know if it's nicer. I wanted to use it because it sounded more idiomatic. But I ended up not knowing because I couldn't find the information.

Filipe Silva17:10:01

yes, the "one way to do it" is the important bit

Filipe Silva17:10:28

if that macro is a reasonable way to do it then it would be better to have it in core.async directly too

Filipe Silva17:10:56

having a "using promises" section that starts with defining a macro is a bit daunting for a novice

lilactown17:10:45

IMO telling people, “thread .then / .catch is the default, we don’t have native async/await. If you want to take on more complex async programming in your apps, use core.async” would be better than suggesting core.async without qualification to ClojureScript beginners.

✔️ 8
didibus17:10:41

That's what I normally tell beginners. Just use .then, and maybe look into promesa.

dnolen17:10:56

I don't really agree w/ that because all this talk is about JS-centric perspectives

✔️ 12
dnolen17:10:03

in the end that's not really what ClojureScript is about

dnolen17:10:10

ClojureScript is Clojure

dnolen17:10:21

and Clojure programmers are fine w/ core.async

lilactown17:10:31

I’m getting more entrenched in this as I talk about it so I might pull out of this convo lol. I feel like it’s if someone were to tell beginners to use RxJS to handle promises in their app.

dnolen17:10:37

every ClojureScript programmer is a potential Clojure programmer

dnolen17:10:04

this kind of emphasis on host-y stuff is not something we're going to do

dnolen17:10:32

cljs.core.async.interop seems fine because that's what it is

Filipe Silva17:10:42

Is this something that exists currently? I can't seem to find it in http://cljs.github.io/api/ or references to it on google

dnolen17:10:40

JS-centric stuff for Clojure programmers

lilactown17:10:29

Clojure is different because you don’t use core.async for one-shot deferred values like we’re telling people to use core.async for in CLJS

dnolen17:10:57

I do that all the time in Clojure

dnolen17:10:21

gotta run for a bit - but will checkin later

lilactown17:10:18

if someone said they wanted to do an HTTP request in Clojure, I’d tell them to use either a blocking call or a future. I’d only pull in core.async if they had something that would actually leverage channels’ multiple-values-over-time and that wasn’t IO heavy in the channel execution.

lilactown17:10:13

again, looking at this from the viewpoint of someone who isn’t using core.async and wants to make a bunch of HTTP requests. they don’t need or want core.async. they want a future. same thing in CLJS: they want an easier way to write promises, not to learn core.async.

chepprey17:10:05

How bad does it look to use core.async for one-shot HTTP req's?

bfabry17:10:00

something like

(let [result (thread (http/get ""))]
  (println (<!! result)))

bfabry17:10:16

in cljs that would be go instead of thread and <! instead of <!!

isak17:10:09

I haven't found channels that useful in UI programming, because usually you want to visualize all of the state. For example, in Pike's 3 request /w timeout example, you may want to show the state of each of those requests on the screen, in which case app-state is coordinating stuff, no channel seems that helpful.

chepprey17:10:07

That's it? doesn't really seem that bad. Not like I need to become a deep guru of CSP multi-channel architecture. (re: core.async one-shot HTTP req)

lilactown17:10:04

in CLJS it would be:

(go (let [result (p->c (http/get ""))]
      (println (<! result))))
but the cost isn’t in the code you write, but now at some point you will need to learn about how core.async works. e.g. how to handle errors, the syntax rules for go and <!, and deal with bugs

👍 4
bfabry17:10:52

imo getting used to using core.async rather than using futures makes sense, because then you’ll be more familiar with the powerful tool when you need it, rather than investing in learning the intricasies of future which is comparatively less powerful. I see the point that it’s extra to learn though

bfabry17:10:09

futures also have complicated error handling to learn 🙂

chepprey17:10:28

@lilactown - the p->c is required?

lilactown17:10:50

assuming that http/get returns a promise, yes

Derek17:10:03

Well https://github.com/r0man/cljs-http returns a channel to start with

lilactown17:10:12

yes and I hate that library because of it

bfabry17:10:23

I was imagining a theoretical synchronous http/get, sorry not very clear

lilactown17:10:48

yeah there isn’t such a thing as a synchronous http/get in CLJS

bfabry17:10:38

yes, my initial example was clojure, as that was what david was talking about saying he does all the time in clojure

lilactown17:10:05

right, I was showing how it would look in CLJS was all

bfabry17:10:34

aaaanyway. core.async rocks. imo learn it, use it, love it. in a crazy pervasively asynchronous world like inside javascript I couldn’t imagine getting along without it

☝️ 4
dpsutton17:10:35

some of this surprised me. Clojure prides itself on having a strong host interop and being a hosted language. So a shyness regarding js promises feels a bit counter to the ideal of embracing the host

👍 12
dpsutton17:10:14

i don't want to weigh in on either side. this isn't a problem i have a strong opinion on. But defaulting to async over interop with promises kinda surprised me is all i'm saying

lilactown17:10:05

I get where dnolen is coming from from the viewpoint of a maintainer. adding support for JS async / await does not have immediately obvious benefits. IMO it’s in the pedagogy, and flattening the abstractions we have to layer on top of our applications, in order to meet the standards that people set for languages today, that make me feel like it’s worthwhile. but it is a non trivial amount of work AFAICT to get async / await to work with today’s CLJS compiler

👍 4
lilactown17:10:09

the first problem is that people are coming from JS, which sways to the whims of whomever is loudest frequently, and they go “where’s my async / await? I expect modern languages to have async / await”

didibus17:10:21

Can closure compiler backport async/await ?

Filipe Silva17:10:32

@dnolen I can give my concrete example since it's very fresh in my mind. I'll put it in a post to not spam the channel.

bfabry17:10:34

you definitely don’t need to understand those macros to learn core.async 🙂

Filipe Silva18:10:51

maybe, but knowing that also doesn't help me

bfabry18:10:09

just wanted to reassure

Filipe Silva18:10:21

now I have a macro I don't understand, core.async that I don't understand, and a promise that I still need to use

Filipe Silva18:10:31

I totally imagine there's a really nice way to do it

Filipe Silva18:10:41

but I do not know it

chrisulloa17:10:53

i think for someone that hasn’t worked with async/await, it’s the same learning curve just to start with core.async… there are also people who have worked with core.async in clj and find it easier to work with in cljs because it uses the same abstractions. just my personal experience.

✔️ 8
chrisulloa17:10:46

also not every modern language has async/await, if you’re coming from go for example it would be easier to work with core.async

lilactown17:10:39

honestly I would expect that if someone hasn’t worked with async / await, chaining .then is probably just as good. if you have used core.async, like core.async and want to use it, then you can! if you have used async / await, like async / await and want to use it, then…? telling people to use core.async is IMO not a good plan if what they want is to do “normal JS stuff”

didibus18:10:12

As a Clojure leaning dev (meaning minimal cljs, mostly Java Clojure) I think the issue is that everything has to be thought of from the viewpoint of Clojure the language and its family

didibus18:10:12

Clojure isn't exactly a Lisp for JavaScript.

didibus18:10:47

It's an opinionated language with paradigms choices. So the question has to be, what's best for working with async APIs?

didibus18:10:57

Not, what does JS do

lilactown18:10:14

neither is it a Lisp for the JVM, but it does include future, locking, etc.

bfabry18:10:57

I’m not sure it would include those fns had core.async existed when they were written

didibus18:10:14

Yes, but it doesn't include all of Java's concurrency primitives. So I can only assume Rich was selective

didibus18:10:29

In that he'd want a Clojure native to still have future

didibus18:10:45

It wasn't a choice made to interop with Java libs

Filipe Silva18:10:08

Worth mentioning that if you do CLJS you can conceivable only learn promise interop without understanding core.async but the reverse isn't true.

☝️ 4
Filipe Silva18:10:36

even if you use core.async with promises you have to understand promises

Filipe Silva18:10:23

and if you come from JS to CLJS the answer to using async/await shouldn't be "don't use it, use .then instead"

Filipe Silva18:10:47

even if it's better in some ways it just sounds backwards

Filipe Silva18:10:05

cljs has macros for syntax, surely it should be able to make that work well out of the box

Filipe Silva18:10:26

await was just macro-like structures until recently

Filipe Silva18:10:46

until last year or so it was uncommon to have native await support

Filipe Silva18:10:07

ts/babel and all those simply downleveled it to a convoluted promise base structure

lilactown18:10:50

the problem I have with telling people to use core.async is you now have to understand promises and channels

lilactown18:10:12

I think that a macro can go a long ways, but there was a thread awhile back where jmlsf went into the deficiencies of macros for this (specifically the deficiencies in promesa, but some of this is inherent to macros): https://clojureverse.org/t/async-generator-functions-in-cljs-requesting-feedback/2262/22

Filipe Silva18:10:58

the rxjs example that was mentioned (by you I think?) is very apt

Filipe Silva18:10:34

Angular forces usage of RxJS in the framework. This was roughly 4 years ago. On polls about topics people want to see in the main angular conferences, beginner RxJS is still one of the top 5 topics. Almost no one does it right. Many conference talks are still about beginner RxJS. One of the most common usages of RxJS is to get an observable and call .toPromise() on it.

lilactown18:10:42

I hopped on the RxJS hype train right around then, and eventually just got fed up with it because I almost never needed the power it was giving me, while constantly having to fight its inherent complexity

lilactown18:10:40

I feel the same way with core.async / CSP. I think it’s better than RxJS for many cases, but most people just need a way to do some flow of async IO that is much easier to do with promises than something that makes operating over multiple values over time first class.

Filipe Silva18:10:56

I ended up using a lot of rxjs and it definitely made me realize how a lot of thing were better with rxjs

Filipe Silva18:10:30

but the sheer amount of work that it takes to do it, and the way it absolutely massacres stack traces... make it onerous

bfabry18:10:51

I think the comparison is a bit unfair. core.async is way smaller and easier to learn than rxjs imo

bfabry18:10:55

have y’all read david’s blog posts on core.async? they’re really good. the first one is “Communicating Sequential Processes” https://swannodette.github.io/

Filipe Silva18:10:31

I was under the impression it was possible with the core.async based macro that David Nolen posted here around 1h ago

lilactown18:10:38

macros will always be an 80% solution, core.async or not

Filipe Silva18:10:53

I guess I have some reading to do, this one and the one lilactown mentioned just now

darwin18:10:56

Ok, the article speaks about emitting async/await js code and I thought you mean that. You can probably implement something with similar semantic as async/await, on top of core.async, raw promises or something else.

lilactown18:10:20

the benefits of emitting async/await JS code is wayyyyy better stack traces, and tooling support in devtools

darwin18:10:25

but for me, implementing core.async on top of native async/await would be enough for better dev experience

lilactown18:10:03

:thinking_face: I am not sure core.async could be built on top of async/await

lilactown18:10:11

oh but on top of generators… perhaps

darwin18:10:18

yep, not sure if could be possible

lilactown18:10:28

the IIFE problem in shaun’s post is one that is annoying me right now for other reasons

darwin18:10:45

I guess, today it would need parts implemented in raw js since cljs does not emit async/await

lilactown18:10:14

funnily, I think this might be able to be ameliorated by adopting another modern JS feature in our output: let / const

Filipe Silva18:10:35

Ah yeah native async/await is a different beast. But transitioning to emitting native async/await is usually a tradeoff with support for legacy runtimes.

darwin18:10:04

google closure should handle that

Filipe Silva18:10:07

even today if you want to use full ES2015 it is advisable to also ship a ES5 build for both frontend apps and node libs

lilactown18:10:25

the CLJS compiler emits a lot of IIFEs in order to guard against bindings being hoisted and clobbering things

lilactown18:10:41

yeah, GCC can already transform async/await a la babel

Filipe Silva18:10:43

I hadn't considered the downleveling in GCC. Yes you are right.

lilactown18:10:08

which makes it much nicer for all of us, because then we can tune our JS bundles to the specific platforms we need to support

👍 4
Filipe Silva18:10:09

it's a good strategy overall, emit at the latest language target supported by the downleveler, then allow the downleveler to do it's thing

👍 4
Filipe Silva20:10:03

actually, doesn't this allow emitting async/await?

Filipe Silva20:10:27

I found it in the link @U08E3BBST put up at the start of the thread

darwin20:10:37

no, this can compile newer js to older js, not vice verse

dominicm21:10:23

go / <! is essentially a macro version of async await isn't it?

darwin21:10:18

sort of, the idea of my post was that cljs cannot generate js code containing async/await, not that you cannot implement something which looks like it

didibus04:11:35

You would have to first implement an async/await macro in my opinion. Make its usage ubiquitous. Port it to Clojure as well. And then demonstrate that the macro should have special handling in the cljs compiler so it is optimized further into native async/await JS code. For that, you'd need to show that the work required is worth getting nicer stack traces and maybe slight performance gains.

didibus04:11:45

I'm not sure the compiler work would be worth the effort tbh. Given the small number of contributors and everything else they could work on instead. But still.

didibus04:11:13

Doing it that way would mean that ClojureScript is still Clojure.

leonoel07:11:57

I share this opinion. It's somehow ironical to have so much requests for implementing async/`await` at the compiler level, and at the same time nobody willing to write and maintain a library to do that in macro space.

leonoel07:11:23

I totally understand the arguments about clean stacktraces and hypothetical performance gains, but keeping a sound language design is a valuable target as well.

lilactown16:11:14

TBH whenever that post comes up I always am more interested in generators than async

lilactown16:11:36

at least async fns are just promises, which interop very well w/o fancy syntax

didibus17:11:47

Well speaking about that, leonel has a very nice coroutine lib that let's you implement imperative generators

lilactown17:11:23

I’m less interested in imperative generators as a concept and more interested about potentially being able to interop with JS iterators/generators

didibus17:11:48

That said, I also want to point out you can do functional generators as well, using Clojure's lazy iterators as explained here https://www.rubberducking.com/2018/04/overview-of-clojurescript-110-features.html?m=1#iterators-and-lazy-sequences

lilactown17:11:20

yes, if i were to do this just in CLJ(S) I would just use lazy seqs/`iterator`

didibus17:11:40

Oh, ya I don't know about interop. Aren't they just an object in JS ?

lilactown17:11:57

but interesting part is being able to use JS generator functions, or hand CLJS built iterators/generators to JS

lilactown17:11:17

sure, but if we’re talking about implementing the concept, I want to be able to leverage the platform

didibus17:11:17

Hum... I'm just trying to think here. If they're just an object of a particular interface, it should be possible to create them in cljs no?

lilactown17:11:09

yes probably

didibus17:11:17

That's a good use case though. I do understand interop challenges can be annoying. I'm still waiting for interop with Java functional interfaces and streams. But like leonel said, I think long Hammock time is needed to also make sure Clojure maintain consistent good language design. Those things can be at odds

didibus17:11:16

It does seem Generators are just a JS object adhering to some interface.

lilactown17:11:32

I think that what I want and what leonel wants are a bit at odds. I don’t think they have to be contrary, but I think that we are likely to disagree about certain things

lilactown17:11:38

leonel has done some amazing work with missionary and cloroutine to create some beautiful abstractions and the capability to implement a lot of these concepts we’re talking about in a cross-platform, Clojure-first way

lilactown17:11:40

however, there are risks with that. in the same way that core.async maintenance has fallen to the wayside, those projects are currently a one-person shop.

lilactown17:11:32

meanwhile, every browser that I want to support today has first-class support for generators and async/await. and my applications will continue to get faster and more support as browsers improve upon those features

👍 4
lilactown17:11:39

I am perfectly happy using different abstractions, libraries, etc. between Clojure (threads/core.async/missionary/etc.) and ClojureScript (promises/generators/etc.) if it means I get best-in-class features and support

lilactown17:11:46

so it’s frustrating when people say, “we can just build async/await ourselves! we can create our imperative generators! etc.” when like, I might have to take over maintenance of that code if my app depends on it and life happens to the author. or I have to build it from scratch and support it myself to suit my use case… when if I could emit certain JS code I would get it “for free”

didibus17:11:55

I'm with you that leveraging existing tooling if possible is a smart move

lilactown17:11:56

I understand that we want to keep the core Clojure language tight and be careful about introducing things willy-nilly. but what I really want is an escape hatch to be able to leverage these new platform syntax features which get better first-class support than what we can provide in Clojure. that’s my thesis.

didibus17:11:38

But I think I disagree with the conclusion. The compiler team is also a one man show pretty much

didibus17:11:06

So all that applies here as well.

didibus17:11:25

Emitting those JS code is non trivial work in the compiler

lilactown17:11:33

I agree, which is why I’ve stated above this thread that I empathize with dnolen’s position

didibus17:11:50

Which may get stale, might not keep up with the pace of JS, might make it harder to implement future Clojure behavior etc.

lilactown17:11:25

JS moves fast but it does not break things, which is nice

lilactown17:11:29

the language, not the libs I mean 😛

lilactown17:11:36

I’m willing to accept that “add async/await/function*/yield to CLJS” is not the right move. so as I’m thinking more, what I really want is the ability to extend the CLJS compiler to emit these new features

lilactown17:11:10

I understand at a high level this would require a significant rework of the compiler. I think that it will save us all a lot of time and energy arguing over this crap as the JS spec continues to grow

lilactown17:11:34

e.g. it’s currently impossible to extend a true JS class today in CLJS

lilactown17:11:32

I’m not demanding these changes. I think it’s OK to say “yes that’s a good idea, no we can’t do it”

lilactown17:11:40

maybe someone will come along who has the capacity to

didibus17:11:27

Ya, a lot of this is us Clojurian are required to get by with little resources. Luckily, it seems applying the 80/20 rule generally has allowed us to keep up with everyone else. And the number of smart people in this community even meant often we were ahead.

didibus17:11:50

I like the idea of an escape hatch. In Java land, you can actually write code that generates Java byte code or java source code. I've done both of those in the past. It does allow an "escape" hatch in that sense. You can also just mix and match java files with clojure files, and the one class per file and everything being a file in Java does mean it is pretty clean doing so. I'm not sure about CLJS

didibus17:11:26

In JS, you don't really have each function or each object be its own file. Maybe having a way to embed JS in CLJS would be a good to have.

lilactown21:11:07

in certain ways, JS is more free and loose. I’ve written ClojureScript code to generate JS, but it was not concerned with code size or advanced compilations or interoping with the rest of the CLJS compile toolchain in any way

Filipe Silva18:10:07

I don't think the size is the important part of the comparison. The promise/rxjs situation was (is) one where people that know promises feel they have to learn and use something else that looks more complicated to do things they found pretty easy to do with promises. Even if it ends up being better, it's still an overhead. And overheads for beginners are very costly. Kinda like the syntax error problem described in https://elm-lang.org/news/the-syntax-cliff.

lilactown22:10:51

pro tip: for peeps using devcards and React/Reagent, use an error boundary around the component you’re iterating on to avoid blowing up your whole page when you accidentally throw an error inside your component

lilactown22:10:45

example in Reagent:

(defn- err-boundary
  [& children]
  (let [err-state (r/atom nil)]
    (r/create-class
      {:display-name "ErrBoundary"
       :component-did-catch (fn [err info]
                              (reset! err-state [err info]))
       :reagent-render (fn [& children]
                         (if (nil? @err-state)
                           (into [:<>] children)
                           (let [[_ info] @err-state]
                           [:pre [:code (pr-str info)]])))})))

(defn my-component []
  (throw (js/Error. "Oops! 👻")))

(dc/defcard-rg
  my-component-card
  [err-boundary
   [my-component]])

👍 12
sova-soars-the-sora22:10:49

oh hey that's smart

sova-soars-the-sora22:10:50

where do i deposit bitcoins

😂 4
💪 4
yedi23:10:11

hey all, im trying to rerun a cljs app that was built 5 years ago, and i'm coming across this error: Uncaught Error: Undefined nameToPath for goog.math.Integer

yedi23:10:45

anyone have suggestions for how id go about debugging that? seems like cljs is having trouble requiring the goog.math module

darwin23:10:41

my wild guess would be that goog.math used to be included by default and your code relied on it

darwin23:10:27

try to require it somewhere at the top level or near the namespace which is raising the issue

yedi23:10:22

did it use to? i can't find anywhere in the code where im directly using goog.math, maybe its a problem with one of the outdated libs im using

darwin23:10:14

so someone who is using it must be also properly requiring it

darwin23:10:02

I think, you can check in your devtools console if the namespace was loaded or not

darwin23:10:24

also you should be able to get whole stack trace leading to that exception

yedi23:10:41

yea im tryna help someone remote debug who's not that familiar with clojure. ok but i guess ill tell him to get devtools set up

dpsutton23:10:37

by devtools i think he means just the js console. not really something to set up? unless i'm mistaken

darwin23:10:56

correct, just chrome devtools console

yedi23:10:41

oh it doesn't have the cljs stack trace, just the js one

yedi23:10:44

and it's not that helpful

yedi23:10:06

Uncaught Error: Undefined nameToPath for goog.math.Integer
    at visitNode (base.js:739)
    at visitNode (base.js:737)
    at visitNode (base.js:737)
    at Object.goog.writeScripts_ (base.js:753)
    at Object.goog.require (base.js:459)
    at (index):38
visitNode @ base.js:739
visitNode @ base.js:737
visitNode @ base.js:737
goog.writeScripts_ @ base.js:753
goog.require @ base.js:459
(anonymous) @ (index):38

yedi23:10:14

apparently a hard refresh fixed it

yedi23:10:31

i hate coding

dpsutton23:10:05

you prefer the bug persisted? 🙂

😅 4
dpsutton23:10:13

just kidding. non-determinism is never fun

darwin23:10:19

the bug might still be lurking somewhere, you just found a workaround 😉