Fork me on GitHub
#clojurescript
<
2019-08-22
>
dog01:08:26

quick question guys, im having trouble with the Calva repl & shadow-cljs it seems like the console is hidden from me:: I can only get the result of evulations, ie 2 gives me back 2, but (clojure.pprint/pprint 2), gives me back nil, instead of 2 nil

dog01:08:44

has anybody ran into this issue before? Thanks! ^_^

sova-soars-the-sora03:08:43

is there a way to compile a cljs app into a standalone js file ?

scknkkrer03:08:26

Yes. ‘Optimizations’ compiler key is what you’re looking for.

🐝 4
sheepy 4
pez05:08:31

@slack1224: it's a known issue. The output only goes to the console atm.

thheller07:08:47

@slack1224 @pez well the return value will always be nil since it prints to *out*. if you want the actual result as a string you can use (with-out-str (cljs.pprint/pprint {:foo "bar"}))

thheller07:08:16

[2:1]~cljs.user=> (require '[cljs.pprint :refer (pprint)])
nil
[2:1]~cljs.user=> (pprint 1)
1

nil
also works fine for me?

pez09:08:16

Not in Calva. The out nrepl message comes with a fixed (always the same) message id and not with the message id of the eval operation. Calva expects the latter and thus fails in showing the output.

pez09:08:20

Figwheel used to behave like that as well, I have been informed. Is it intentional in shadow-cljs, or should I file a change request? (This is a bit tricky to solve in Calva, because without the message id of the eval, I don't know where the output should go.)

thheller10:08:31

why is everyone treating nrepl differently? and why do :out messages need to be routed in the first place? it is just output that you should print?

pez10:08:28

I don't know enough about this to have an opinion on wether nrepl should be treated differently or not. 😃 As for why it needs to be routed, in Calva the user can evaluate things either inline in the editor, and that is ”mirrored” in an output log, where also any printing side effects show up. Or he can evaluate things in the REPL Window/prompt and then results and output are printed there. I might have wired things up incorrectly, but I do have the routing problem as it is.

thheller12:08:02

well in JS we have the async problem. so I can capture all prints DURING evaluation easily but I cannot capture prints from async events that way

thheller12:08:20

doesn't matter in the case of pprint but it does matter as soon as anything async is done

thheller12:08:42

(-> (js/fetch "/foo") (.then prn)) where should that print?

thheller12:08:59

the out binding of the initial eval is already gone

thheller12:08:57

(curious to hear what figwheel does)

pez12:08:59

I can try figure out what figwheel does.

pez12:08:29

If I can get the prints that happen during the evaluation in the eval response message, a lot would be won.

thheller12:08:23

well isn't that any message you received after sending the eval request and before receiving the response?

pez12:08:27

I also have to fix the case you mention with any printing that happen after the evaluation.

thheller12:08:36

I mean you have the ordering control in your end?

thheller12:08:18

I'm not saying that the out/err handling in shadow-cljs is great. just saying that we have to account for async since almost everything is in JS

thheller12:08:35

well not almost everything ... but a lot 😉

pez12:08:02

I am planning to add a general output message handler. This driven by the case where some evaluation spawns of a thread where things are printed.

pez12:08:53

But I would like to be able to treat in-eval-printing such that I can print that at the same place where I print the results of the eval.

pez15:08:41

Now tried your example with (-> (js/fetch "/foo") (.then prn)) with Figwheel Main. It comes back with the same message id and session as the eval. This is what I get back:

{"id":"14","ns":"cljs.user","printed-value":1,"session":"eae3908b-0bdb-43a0-a883-5eb182d8b4f6","value":"#object[Promise [object Promise]]"}
{"id":"14","session":"eae3908b-0bdb-43a0-a883-5eb182d8b4f6","status":["done"]}
{"changed-namespaces":{},"id":"14","repl-type":"cljs","session":"eae3908b-0bdb-43a0-a883-5eb182d8b4f6","status":["state"]}
{"id":"14","out":"#object[Response [object Response]]\n","session":"eae3908b-0bdb-43a0-a883-5eb182d8b4f6"}

pez16:08:49

The Clojure REPL also sends back the out messages with the same message id as the eval itself.

pez16:08:18

(With shadow's clj repl)

thheller16:08:17

can you quickly test what happens in figwheel if you eval something else before the promise fires?

thheller16:08:24

(-> (js/fetch "/foo") (.then prn)) 1

thheller16:08:46

or that might just be treated as one eval

pez16:08:26

afk atm. Will check.

pez17:08:38

{"id":"14","ns":"cljs.user","printed-value":1,"session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5","value":"#object[Promise [object Promise]]"}
{"id":"14","session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5","status":["done"]}
{"changed-namespaces":{},"id":"14","repl-type":"cljs","session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5","status":["state"]}{"id":"14","out":"#object[Response [object Response]]\n","session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5"}

pez17:08:51

With (do (-> (js/fetch "/foo") (.then prn)) 1) I get:

{"id":"20","ns":"cljs.user","printed-value":1,"session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5","value":"1"}
{"id":"20","session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5","status":["done"]}
{"changed-namespaces":{},"id":"20","repl-type":"cljs","session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5","status":["state"]}
{"id":"20","out":"#object[Response [object Response]]\n","session":"2e40e1e2-d1d3-4c2e-a212-4cf5d05c0ab5"}

thheller19:08:38

hmm where is the #object[Promise [object Promise]]? thats just dropped?

restenb14:08:37

is this correct interop? this.userOptions.plotOptions.pie.innerSize -> (.. this -userOptions -plotOptions -pie -innerSize)

Lone Ranger14:08:51

does anyone know any examples of well-known consumer facing clojurescript web applications?

enforser14:08:13

I think CircleCI is written almost entirely in Clojure(script) https://github.com/circleci/frontend

Lone Ranger14:08:01

is that just the landing page and portal or is there some kind of web tool / webapp also?

nwjsmith14:08:42

The landing page is not ClojureScript, but the web application is mostly ClojureScript

Lone Ranger14:08:32

looks like precursor is also. It's actually a pretty cool app, too! https://precursorapp.com/

nwjsmith14:08:50

Whimsical is ClojureScript as well: https://whimsical.com/

Lone Ranger14:08:46

Interesting... I wonder if these apps were developed with "hey what would be a natural choice for a product using clojurescript?" or more of a "we want to do this complex workflow app and the only thing that makes sense is clojurescript", or if it was something more organic

Lone Ranger14:08:00

does anyone happen to know what lucidchart was written in?

nwjsmith15:08:37

Not sure about Lucidchart, but Precursor was developed by some ex-CircleCI employees who had acquired CLJS experience from working on the CircleCI frontend.

dnolen16:08:17

note that CircleCI has moved off ClojureScript as far as I know - from what I understand the team got significantly larger and they wanted to be able to just hire React devs

dnolen16:08:04

@goomba you can be effective with most modern tools for UI programming - claims otherwise are lies

dnolen16:08:32

ClojureScript / React simply show that functional techniques can be applied to UI programming at scale

dnolen16:08:59

so if you and your team prefer FP techniques then ClojureScript is for you

dnolen16:08:58

it's like JavaScript vs. TypeScript

dnolen16:08:12

there's no evidence that doing it one or another is going to make one bit of difference for any product

Lone Ranger16:08:31

you're too modest @dnolen!! I think there's something very special about ClojureScript

dnolen16:08:50

thanks, but really it's quite true

dnolen16:08:08

programming languages are kind of currency between a team

dnolen16:08:35

sometimes there are legitimate technical reasons - but I've rarely found that to be true in narratives people tell

dnolen16:08:17

the most powerful story for ClojureScript is the fullstack one

dnolen16:08:29

i.e you're doing Clojure on the backend and ClojureScript on the frontend

Lone Ranger16:08:37

True. I just found myself in a similar boat to Mr. Hickey... if I had to write one more line of JavaScript I was going to quit and go back to potato farming

dnolen16:08:39

and in your mobile apps, and in other JS contexts

dnolen16:08:20

if you're sick of the programming language hodge-podge here Clojure(Script) provides an escape

Lone Ranger16:08:03

While I agree that "anything can" be done with any client side language, I feel that the full stack synergy you mentioned hasn't been fully explored -- in fact I think we're barely scratching the surface. I'm sure you've checked out Tonsky's Web After Tomorrow blog entry?

Lone Ranger16:08:25

I can't think of a single site that's doing that.

dnolen16:08:29

I've worked on Clojure(Script) + Datomic projects - lot of fun and sure IMO less complexity due to a unified programming model across all the bits of architecture

dnolen16:08:45

but I've also worked on good / bad projects using that stack

Lone Ranger16:08:33

Well, the welcome-aboard kit with Clojure(Script) comes with enough rope to hang yourself, if that's the route you want to go, but I appreciate that 🙂

Lone Ranger16:08:37

I think the killer missing piece right now is WebWorker-side VDOM diffing

Lone Ranger16:08:45

After that it's game over

dnolen16:08:07

to be honest I don't really think all this super heavy client stuff is all that interesting

Lone Ranger16:08:26

😮 😮 😮

dnolen16:08:56

there's a sweet spot between client/server

Lone Ranger16:08:03

What's up with all this modesty, wisdom, and pragmatism hahaha

dnolen16:08:05

and lot of projects make a mess on one end or the other

dnolen16:08:02

but really the real complexity is team siloing

dnolen16:08:11

i.e. frontend vs. backend

dnolen16:08:20

so Clojure(Script) just removes that

dnolen16:08:26

but then you still have a lot of problems to solve

Lone Ranger16:08:34

yes/no... mm... that's a big problem with Clojure(Script) in general, and I think why teams eventually port their stuff to more accessible aka popular languages that are more copy/paste friendly and require less engineering discipline -- but I have the luxury of being incredibly biased

Lone Ranger16:08:00

It's more of a LISP problem than a Clojure(Script) problem

dnolen16:08:30

IMO popular languages are about hiring

dnolen16:08:42

i.e. your team is so big and there's so much process, siloing is a given

Lone Ranger16:08:04

If you hire a Django dev or a Ruby on Rails dev you know what you're getting and everyone speaks the same language.

Lone Ranger16:08:30

Kind of like why they went from archers to crossbows back in the middle ages. Doesn't take 20 years to train someone on the crossbow (edit: I have no idea if this is true or not but why let the facts get in the way of a good story...)

dnolen16:08:04

I think modern projects look a bit different

dnolen16:08:12

you hire a Django Dev and you hire a React dev

dnolen16:08:01

and you try to avoid impedance mismatches in frontend / backend

dnolen16:08:46

in Clojure - you can often just hire a Clojure dev

dnolen16:08:01

and they can cover the architecture

Lone Ranger16:08:55

That impedance mismatch, weirdly enough, has been one of the awesomest things about cljs for me in doing full-stack work. It's been a survival tool in large inflexible organizations. Sometimes I can get more done client-side than is possible server-side just due to architectural or framework limitations.

Lone Ranger16:08:22

Also oddly enough the nieche of cljs avoids a weird "bikeshedding" problem -- with popular frameworks/languages sometimes devs love to get into weirdly divisive arguments over minutia ... "is this function Pythonic enough?". With cljs in big organizations its, "I don't fully know how it works but darn you're doing a good job of delivering so keep it up"

👏 4
Lone Ranger16:08:14

Anybody else care to hear any more opinionated philosophy? 😛 picard-facepalm

dnolen16:08:39

right, sure - that can work in some places

dnolen16:08:52

and Clojure has that benefit too

dnolen16:08:13

but I think a wider success story probably needs to attack from a different direction - that's all I'm suggesting

Lone Ranger16:08:32

I wonder if that wider success story is possible, or if it's closely tied to the so-far unsolvable riddle of "Quantitatively, what makes for a good developer?"

Lone Ranger16:08:09

Probably more like a bhuddist koan though ... I'm not sure a good developer exists in isolation

dnolen16:08:17

well we've had more success than I imagined possible for a Lisp - so we'll see 🙂

Lone Ranger16:08:07

Indeed. For what it's worth, you and your team have certainly made my life better.

Sam Ferrell17:08:34

cljs is the only reason i have fun doing side projects... maybe one day i will be able to sneak some into production

David Pham17:08:57

I have to say that CLJS makes UI learning an enjoyable experience.

David Pham17:08:21

The hot reloading makes the learning fun.

David Pham17:08:30

And also the repl.

Lone Ranger17:08:37

Indeed. The first time you get that nrepl going and you see your code hot reload -- that's a unique experience to ClojureScript as far as I know.

Lone Ranger17:08:01

Of course it took me about 3 years to get the nrepl going but I'm happy I finally got there!

David Pham17:08:17

Shadows-cljs made it much simpler

Lone Ranger17:08:36

You can do it fully out of the box with deps.edn now

David Pham17:08:38

Obviously, having experience in Emacs and coding in Clojure for fun for a few years helped that.

David Pham17:08:24

I dunno about deps.edn, but I know shadow-cljs helped me with all the major steps to understand the process to ship a UI.

David Pham17:08:52

The biggest issue is only the beginner’s curse of choice.

David Pham17:08:51

@dnolen how do you see if there are too much work on client or server side? How does that situation arises?

dnolen17:08:39

on the server side you have stuff like the old Rails Turbolinks, on the client side you have people just trying to do everything in the client (no pages, etc.)

David Pham17:08:25

What does it mean no pages?

dnolen17:08:04

that's I mean no real pages

dnolen17:08:12

SPA - single page app

alpox17:08:47

Is there a way to transform clojurescript forms (preferably fn) to the most simple form of a javascript function so I could produce a javascript function in isolation? I am asking this because pouchdb takes a simple javascript function as string for its map/reduce approach. I could just write the functions as javascript strings but it would be nicer to be able to have a simple transform (Without CLJ internals)

David Pham17:08:30

So SPA are bad?

Lone Ranger17:08:36

I think the SPA is tempting because currently setting up a .js file per page like one might normally do in a typical full stack site carries with it a great deal of inertia, and creating reusable libraries while using advanced compilation has a higher barrier to entry than normal js projects

Lone Ranger17:08:52

Not necessarily bad just harder to get right

Lone Ranger17:08:22

When you have multiple pages you aren't bound by the decisions of other pages, it's just harder to get started

dnolen17:08:46

@neo2551 that's not what I'm saying

dnolen17:08:04

I'm talking about people writing SPAs with zero value - i.e. it's not a good fit for the problem

dnolen17:08:23

@alpox not sure what you mean, ClojureScript functions are JS functions

Lone Ranger17:08:23

means can I port a clojurescript function directly to JavaScript without all the clojurescript machinery

Lone Ranger17:08:55

Just wants the "simple function" equivalent

Lone Ranger17:08:10

As if mapped by hand

dnolen17:08:13

@alpox oh but you need to pass it as a string ... yeah probably simpler to just write the JS

dnolen17:08:41

I had to do this with Puppeteer recently - I just wrote those fns in a Closure namespace, ClojureScript makes it easy to load that kind of JS

alpox17:08:05

Hmm I figured as much but I thought I better ask before ruling this possibility out fully. Thanks for the response 🙂

dnolen17:08:28

@alpox well issue would be you can't use anything from the ClojureScript std lib anyway - so even if we produce simpler output you would be effectively writing JS - you couldn't use any higher level anything

Lone Ranger17:08:47

It's not impossible it just doesn't always make sense. You can advanced compile and specify that you don't want the compiler to munge the name and use the resulting artifact as a native JavaScript function, but to say it's overkill is an understatement

alpox17:08:51

Yes I'd have to strictly stick to the JS world so or so

dnolen17:08:52

and by standard lib I mean you can't use keywords, destructuring etc.

dnolen17:08:23

you would have to be pretty careful - so it's not really a valuable feature

alpox17:08:36

Right, thanks for making up my mind 🙂

Lone Ranger17:08:12

One of the "issues" with cljs is that it doesn't support copy/paste development

Lone Ranger17:08:36

(it's not really an issue)

alpox17:08:02

I guess that is an advantage in reality but can be seen as issue by some

Lone Ranger17:08:55

But you can't get away with just pasting in code, need to understand the workflow. Higher barrier to entry, but not so much more than webpack I think..?

souenzzo17:08:17

With #fulcro and cljc you can develop applications that work both with react/JS and fully SSR(JVM) with exact same code. Or something like develop with react/JS (for hotreload) and deploy just with SSR (once SPA will not delivery any extra-value) There is still some missing parts (routing/docs), but is possible.

metal 4
souenzzo17:08:19

Maybe share code between RN<>SSR There is a (broken) WIP about this here https://github.com/souenzzo/graph-demo

alpox17:08:57

@goomba Webpack is a showstopper in many ways but most of the JS world uses Webpack only through wrappers like CRA now and have to touch it very rarely

Lone Ranger17:08:38

Oh... Maybe I'm a masochist

jpmonettas19:08:47

hi! I'm finding hard to use from clojurescript js libraries where examples are written in ES6, are there any cljs abstractions or libraries for creating classes that extend from other classes, and deal with constructors, super, etc or is just working the prototype chain by hand the current way to go?

dnolen19:08:25

there's no sugar currently - though you have macros and can supply your own - perhaps there's a lib out there

dpsutton19:08:26

I'm getting Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. with the following

[:span.ctx-Button.u-spaceInlineLeftSmall
 (if attaching?
   [:<>  ;; if i replace this with a :span the error goes away
    [cortex/icon-spinner]
    [:span (or attaching-text "Attaching")]]
   (if icon
     [icons/icon :paperclip.fw (or button-text "Attach")]
     (or button-text "Attach")))]
If i replace the react fragment with a span the error goes away and I can hotfix but I don't really understand why this is happening. Does anyone perhaps understand this situation a bit better?

Lone Ranger19:08:15

yes, if I had to guess you're using a component that gets replaced by SVG

Lone Ranger19:08:14

the icons, specifically

dpsutton20:08:23

ah you're 100% correct!

Lone Ranger20:08:33

in other words, the [cortext/icon-spinner] probably starts out as an <i>... and gets replaced by SVG+XML but this happens outside the control of React, and React is complaining during the teardown phase

Lone Ranger20:08:52

so you have two or three options

Lone Ranger20:08:34

first is use non-svg icons, second is find react-friendly svg icons, third is you can create a form-3 hiccup component that handles the teardown part of the lifecycle explicitly, and four is you might be able to bundle up those icons with webpack and it might go away (haven't tested the last one though, it's just a hypothesis)

dpsutton20:08:45

i'm reverting the change back to i tags due to dropped click handlers so I'll get that there.

dpsutton20:08:55

amazing problem solving. thank you so very much

marciol20:08:57

@dnolen you think that makes sense somethink like the Phoenix Liveview in Clojure world?

Lone Ranger20:08:43

@dpsutton haha that error message is burnt into my mind... after hours of tearing my hair out with that problem once I won't soon forget it

dpsutton20:08:19

well thank you very much for preventing the anguish 🙂

4
dnolen20:08:23

@marciol what is that, link?

Lone Ranger20:08:47

General question for some kind of React app -- if the application state is the result of the initial state + event1 + event2 + ... + eventN, what do we call that? Event driven? Event sourced? Data driven?

Lone Ranger20:08:37

And also, what do we call it if we prefer sticking closer to core clojurescript (as opposed to framework heavy?)

Lone Ranger20:08:10

"minimalist" doesn't seem right because sometimes it can be more verbose

marciol20:08:30

Exactly @mkvlr @dnolen that is like a ELM architecture on the server, using websockets to send DOM diffing to browser

dnolen20:08:01

looks pretty interesting - perhaps some kind of happy mix possible here for Clojure(Script)

marciol20:08:16

It’d be awesome

Lone Ranger20:08:14

It is very interesting. I am curious how applications which are structured this way maintain sanity in the long rung... same problem I run into with a lot of distributed microservices, certain distributed and asyncronous work, and that I used to have with certain SPAs. i.e., imagine you have 10 go-loops that wait for certain events to occur (maybe they're listening to an event log, maybe they get sent things from a pub/sub, who knows) and then take action based on those events. It's very easy to write apps like that... you write one of these processes and it's well defined and it does what it's supposed to. But later they can be hard to maintain because the behavior of the application gets more complex over time and sometimes it's hard to remember why the app is behaving the way it is.

Lone Ranger20:08:55

i.e.,

(def proc1
  (go-loop []
   (when (some-event-happens)
     (modify-app-state)
     (recur))))

(def proc2
  (go-loop []
   (when (some-other-event-happens)
     (modify-app-state-again)
     (recur))))

Lone Ranger20:08:07

easy to write, sometimes hard to remember what's going on lol

marciol20:08:16

Yes, seems that they are experimented with this approach, so there is nothing in production yet, but seems very interesting. Follows the example that Bruce Tate twitted recently: https://twitter.com/redrapids/status/1164197019289882625