Fork me on GitHub
#shadow-cljs
<
2017-10-23
>
Jon02:10:03

Being a JavaScript developer I see this issue in a different angle. I don't use Java. So using Clojars is like "using two package managers", since I've been using npm for so long.

Jon02:10:05

Lumo is moving away from JVM. And it sounds more natural to use npm as the package manager, just like all other compiled-to-js languages, despite that npm is far from perfect.

thheller07:10:07

seriously though: if there was a shadow-cljs publish command. would you care where it ended up?

thheller07:10:02

“it is easier to deploy to npm” is not a valid argument to use it since 99% of the time you just want to USE the dependency not deploy it

Jon14:10:04

I should say it is "easier to learn to deploy to npm"

Jon14:10:19

another purpose of use phrasing npm is because I'm so familiar with npm, I can browse node_modules/ and manage it with yarn. I can hardly do anything when I'm not familiar with jar.

Jon14:10:46

even though it's also able to extra jars from somewhere...

Jon14:10:25

and I have many friends familiar with npm, here in China..

mhuebert09:10:52

@thheller when a shadow server connects to the browser, is there a way for the browser’s code to ask the server questions like “what build am i” or issue commands like “build release”? (ie. building blocks for a UI)

thheller09:10:48

shadow.cljs.devtools.client.env might already have everything you need?

mhuebert09:10:03

ok i’ll check there, wasnt sure where to look

thheller09:10:24

shadow.cljs.devtools.client.env/build-id

thheller09:10:54

issuing commands is not yet possible

thheller09:10:07

but I’m happy to create any kind of endpoint you need 🙂

thheller09:10:54

can’t really issue commands from the build itself

mhuebert09:10:07

so repl-call is used to evaluate expressions that you are sending to the browser

thheller09:10:41

well its for the REPL

thheller10:10:28

but none of that should be used if you want to build an actual UI, ie. the UI should not become part of the build

thheller10:10:44

it should be standalone IMHO

mhuebert10:10:46

i am imagining the UI would be a :preload? like re-frame-trace

thheller10:10:21

there is some prior art for the UI I have in mind

mhuebert10:10:08

saw that the other week, it looked nice

Jon14:10:49

no able to access Youtube from mainland China these days. sigh...

mhuebert10:10:32

a benefit of something injected into a build is that you could show warnings/build status directly there, in the window that you’re looking at while developing

mhuebert10:10:39

à la figwheel

thheller10:10:51

a basic HUD already exists

thheller10:10:55

that is a preload yes

thheller10:10:09

but the UI that controls shadow-cljs should not be part of the build itself

mhuebert10:10:50

i don’t think I’ve noticed the hud

thheller10:10:10

its not enabled by default

thheller10:10:15

:preloads [shadow.cljs.devtools.client.hud]

thheller10:10:23

its too ugly

thheller10:10:41

more or less just a test of how it could be done

thheller10:10:53

pretty sure it doesn’t work anymore

thheller10:10:13

let me try and fix it

thheller10:10:19

not a good demo if it doesn’t work 😉

thheller10:10:39

ah no it actually still works

thheller10:10:51

still ugly but it works 😉

thheller10:10:17

ah open in editor link is missing a name

mhuebert10:10:23

transit really needs to fix that uri? issue

mhuebert10:10:51

or did they…

mhuebert10:10:07

there is a new release! 🙂

thheller10:10:23

they did, I bumped it in shadow-cljs already

mhuebert10:10:35

ah i had the old version in maria’s deps

thheller10:10:07

I don’t really like the HUD as a concept

thheller10:10:18

I want to see errors in my editor ASAP

thheller10:10:39

having to look at the browser for that is meh

thheller10:10:36

like that … one day …

thheller10:10:13

this could definitely become nicer looking

mhuebert10:10:25

errors in the editor… is there a standard way to communicate stuff like that to an editor? (ie. something that would work with Cursive and emacs?)

thheller10:10:06

short answer: no.

thheller10:10:15

I spent quite a bit of time in that rabbit hole

thheller10:10:05

thats an attempt at some kind of “standard” for this

thheller10:10:15

quite a few editors support it

thheller10:10:23

cursive or rather intellij doesn’t

thheller10:10:26

> Language server support cannot be combined with existing IDE tooling; it’s an either/or proposition. If IntelliJ used the language server for Java, it would lose 95% of its feature set for Java development.

thheller10:10:37

I don’t think its coming to IDEA anytime soon

mhuebert11:10:17

does appear to be a rabbit hole 😞

mhuebert11:10:52

well, when developing for the browser, warnings/errors there will show up at essentially the same speed as if it was connected to the editor, if you have a side-by-side view on your screen

thheller11:10:59

my setup usually looks like this

thheller11:10:03

“almost” in my editor

thheller11:10:14

need to bug colin so I can send special nrepl messages the UI understands … but he is busy 🙂

mhuebert13:10:13

Last night I was surprised to find that my parsing lib runs ~8x faster with :simple optimisations than in dev

mhuebert13:10:08

Anything to do with perf tweaking/debugging might be another user case for watching release builds

thheller13:10:41

the issue with watch for release is that running it on every file change is too slow

thheller13:10:56

in dev thats non an issue since it usually takes less than a second

thheller13:10:20

but say you are fixing a release bug and touch 3 files to “fix” it

thheller13:10:47

if those 3 files are not saved at once the watch may trigger early

thheller13:10:18

but I can implement it regardless if you want. its not hard to do.

thheller13:10:45

my workflow typically consists of poking at it in the REPL

thheller13:10:52

then when I’m done trigger release manually

thheller13:10:24

release is pretty expensive compared to watch

thheller13:10:39

in cursive you could create a custom REPL command to trigger a release build

mhuebert13:10:44

I should into these custom REPL commands

mhuebert14:10:47

Can't edit on a phone. Sigh.

mhuebert14:10:08

I actually can't find what you mean by custom REPL commands

thheller14:10:37

tools -> REPL -> edit REPL commands

thheller14:10:05

allows you to specify a string of text to send to the REPL

thheller14:10:09

and bind it to a key

thheller14:10:46

ie bind (shadow.cljs.devtools.api/release :browser) to a key

thheller14:10:47

oh the tools -> REPL menu might only show up when connected to a REPL

thheller14:10:52

there was something weird about that

mhuebert14:10:05

I ignore probably 80% or more of IntelliJ.

mhuebert14:10:33

I just discovered Preferences > Appearance & Behavior > Menus and Toolbars

mhuebert14:10:46

Life will never be the same

mhuebert15:10:11

would it make sense to apply :simple optimizations to bootstrap js?

mhuebert16:10:25

this is a great workflow, REPL command with shortcut

mhuebert16:10:50

all the ^boolean type hints seem to have zero effect, in any kind of optimization mode

thheller16:10:26

you can tell if they have an effect by the generated code

thheller16:10:54

hmm wonder if I broke that when I “fixed” the incorrect ^js tag behaviour?

thheller16:10:34

nope not broken, you had me worried there 🙂

thheller16:10:13

all the ^boolean tag does is remove the cljs.core.truth_ call

thheller16:10:55

without you get if(cljs.core.truth_(demo.browser.thing)){...

thheller16:10:18

with you get if(demo.browser.thing){ its only really needed in cases where closure is supposed to kill dead code

thheller16:10:04

and super critical loops maybe

mhuebert16:10:06

is there a particular lib you use for cljs perf testing?

mhuebert16:10:40

for my parse lib, using sequential keyword-identical? comparisons vs. (contains? #{…} the-kw) results in a 2x overall speedup

thheller16:10:00

I use the chrome devtools occasionally to profile

thheller16:10:19

(contains? #{…} the-kw)

thheller16:10:36

did you try (contains? the-set-but-from-a-def the-kw)?

mhuebert16:10:37

just realized i was mistakenly using keyword-identical? with strings. still had correct result, but slightly faster i think to use identical?

thheller16:10:00

bootstrap js and :simple no, then you’d lose the ability to load individuals namespaces

mhuebert16:10:11

but, this is in a really tight loop and i am not sure one can go faster than successive identical comparisons

thheller16:10:44

contains is a hash lookup

thheller16:10:53

just re-creating the set on every call is not good

mhuebert16:10:27

somehow i imagined that a literal set wouldn’t be re-created

thheller16:10:38

yeah the compiler is not that smart

mhuebert16:10:22

re: bootstrap :simple - would not have to pile all the files together?

mhuebert16:10:36

i am just surprised at this 10x speed up using :simple

thheller16:10:05

10x might be from constants

thheller16:10:35

in :none constants are not optimized so every keyword or symbol is allocated

thheller16:10:47

in optimized code its allocated once and shared

thheller16:10:21

if you allocate too much you force a GC

thheller16:10:32

tight loop with keywords might do that

thheller16:10:07

:simple but still separate files gave me an idea

thheller16:10:31

that might be worth doing yes

thheller16:10:22

SSL with self-signed certificates is so annoyingly complicated

mhuebert17:10:23

well, this is interesting. when the parsing lib and sample text are loaded by themselves, the :simple optimization mode makes a much smaller difference, maybe 20%

mhuebert17:10:54

but when the exact same test is loaded as part of Maria’s live build, then it slows down 10x when not in :simple

thheller17:10:44

:static-fns maybe?

thheller17:10:12

hmm not sure I understand. as part of the live build means loaded by the self-host?

mhuebert17:10:26

i don’t fully yet understand either. so the live build is :optimizations :simple, nothing fancy. It does load the bootstrap build but I am not using the compiler to perform this test

mhuebert17:10:57

i’m trying to reproduce and can’t yet. i thought maybe it had something to do with loading it in an iframe, but that doesn’t seem to be the case

thheller17:10:27

I do think iframes get a lower priority but not sure

mhuebert17:10:15

maybe it has something to do with memory usage

thheller17:10:51

how do you guys feel about the shadow-cljs - config: /Users/zilence/code/shadow-cljs/shadow-cljs.edn version: 2.0.35 line?

thheller17:10:58

should I keep it?

thheller17:10:09

trying to trim some unnecessary output from commands

mhuebert17:10:03

i like seeing the version number

mhuebert17:10:06

unsure if path is necessary

thheller17:10:39

echo :foo | shadow-cljs clj-eval --stdin
shadow-cljs - config: /Users/zilence/code/shadow-cljs/shadow-cljs.edn version: 2.0.35
:foo

thheller17:10:05

cat script.clj | shadow-cljs clj-eval --stdin

thheller17:10:33

node packages/shadow-cljs/cli/runner.js clj-eval "(shadow/release :thing) (something-after-release)"

thheller17:10:50

might be useful

thheller17:10:30

shadow-cljs clj-eval "(shadow/release :thing) (something-after-release)"

thheller17:10:38

like this of course

thheller17:10:40

wanted something that lets me string together a few things … like calling rsync after release 😉

thheller18:10:25

:simple for bootstrap is not so simple unfortunately

mhuebert19:10:17

hmm, how does it behave?

thheller19:10:37

the closure compiler doesn’t like compiling like that 😛

thheller19:10:36

thought I can abuse the :npm-module stuff I have already but that doesn’t quite work

thheller19:10:01

------ WARNING #1 --------------------------------------------------------------
 File: cljs.core$macros.js:2

 namespace "clojure.walk" is required in module cljs.compiler.js but provided in module clojure.walk.js. Is module cljs.compiler.js missing a dependency on module clojure.walk.js?
--------------------------------------------------------------------------------

thheller19:10:12

Closure compilation failed with 6 errors
--- cljs.js.js:2
namespace "cljs.core$macros" cannot be provided twice

thheller19:10:29

not really sure what is happening … a bunch of weird stuff

mhuebert19:10:44

hmmmmm so, it appears that :async-require is what’s behind this weird slowdown

thheller19:10:10

really? how does that happen?

mhuebert19:10:06

absolutely no idea! 🙂. but that’s a console log after reloading, only difference commenting out that part in devtools

thheller19:10:59

oh its loading the dead js deps

thheller19:10:07

thats not supposed to happen 🙂

thheller19:10:53

can you try to js/setTimeout the start of the test?

thheller19:10:14

or how do you trigger the test? just on load I assume?

thheller19:10:52

can you try in the :after-load fn?

thheller19:10:08

so its done after the code loading process finishes?

mhuebert19:10:34

my test code is:

(dotimes [n 5]
  (js/setTimeout
    #(time (tree/ast "..."))
    n))

thheller19:10:14

and if you remove the :async-require its normal?

thheller19:10:51

hmm the async require loads code via xhr and then js/evals it

thheller19:10:02

maybe that doesn’t get optimized be the JIT?

thheller19:10:58

did you try with compile and not watch?

thheller19:10:25

yep still relevant

thheller19:10:33

but I can probably do the script injection

mhuebert19:10:45

wow, what weirdness

thheller19:10:30

this is so weird, append a script node and remove it directly after

thheller19:10:36

code still gets eval’d properly 🙂

thheller19:10:04

you are in an iframe right?

thheller19:10:33

I think eval(code) is window.eval(code)

thheller19:10:12

maybe its weird because of the iframe

thheller19:10:44

create a shadow/build/async-closure-import.js in your project source path

mhuebert19:10:03

so this little trick they do:

var createScriptFunction = function(args, body) {
  var prevDash = window._,
      script = document.createElement('script'),
      sibling = document.scripts[0];

  script.text = 'var _ = function(' + args + ') {' + body + '\n}';
  sibling.parentNode.insertBefore(script, sibling).parentNode.removeChild(script);

  var result = window._;
  window._ = prevDash;
  return result;
};
maybe i should do this with all of the eval’d code from the bootstrapped compiler?

thheller19:10:07

don’t want to do a release if it doesn work

mhuebert19:10:15

ok just a sec

thheller19:10:26

function scriptEval(code) {
    var node = document.createElement("script");
    node.appendChild(document.createTextNode(code));
    document.body.append(node);
    document.body.removeChild(node);
  }

thheller19:10:31

instead of straight eval

thheller19:10:54

page loads just fine after

thheller19:10:11

don’t have a benchmark handy I could use to test

mhuebert19:10:29

currently building

mhuebert19:10:10

seems to be somewehre in the middle, ~1800ms

mhuebert19:10:34

i am not sure if things are compiling normally

thheller19:10:38

might be best to stay away from async-require if that fixes it

thheller20:10:03

did quick benchmark myself. 6.5sec without async require, 6.5 sec with async require + script, still waiting for eval 😛

thheller20:10:28

still waiting …

thheller20:10:05

might be a big boost for bootstrap as well if eval is really that slow

thheller20:10:14

still waiting … geez

mhuebert20:10:48

is this code supposed to be running on the shadow-cljs server?

mhuebert20:10:29

i mean shadow-cljs server — does that need to be restarted after adding this js code

mhuebert20:10:04

i have just got into a weird state where the same benchmark code is now running at the midpoint of slow and fast and nothing i do seems to change it.

mhuebert20:10:12

let me dig myself out of this hole 🙂

thheller20:10:20

no you just put the file into your classpath

thheller20:10:26

and trigger a compile

thheller20:10:39

it will basically just be prepended to your build .js file

thheller20:10:09

the difference is crazy .. I had to kill the tab

thheller20:10:12

it just didn’t finish

thheller20:10:45

140ms with scriptEval

thheller20:10:58

11514ms with eval

thheller20:10:01

so crazy 😛

mhuebert20:10:59

is this Invalid SemVer Range something simple to fix?

thheller20:10:24

uh right .. I forgot about that

thheller20:10:35

well I can fallback to just string compare in that case

mhuebert20:10:40

oops typed description in image title

thheller20:10:33

also has the faster script eval for things loaded by the devtools

thheller20:10:49

and the bootstrap/load

thheller20:10:23

hmm I should have done the benchmark in firefox as well

thheller20:10:27

you never know 🙂

thheller20:10:37

also fixed the semver thing

thheller20:10:51

firefox doesn’t care at all

thheller20:10:59

eval is the same speed

thheller20:10:37

nah not quite the same speed

thheller20:10:48

250ms vs 400ms

thheller20:10:00

vs chrome 11sec 😛

thheller20:10:50

how is this not a widely known thing?

mhuebert21:10:20

i don’t know but it’s nuts

mhuebert21:10:09

and i don’t understand the logic. they don’t prevent dynamically supplied code from being optimized, they just make it awkward?

mhuebert21:10:17

maybe just a low priority thing.

mhuebert21:10:31

not really intentional.

thheller21:10:57

eval is on that list without comment

thheller21:10:01

no idea what it means

mhuebert21:10:08

wow, didn’t realize that about try-catch

mhuebert21:10:20

Don't put try/catch inside computationally intensive functions.
You could try { test() } catch

mhuebert21:10:29

oh, this page is more recent: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers Functions that contain a try-catch statement (Optimized in V8 commit 9aac80f / V8 5.3 / node 7.x)

mhuebert21:10:58

says that functions that call eval() are ‘likely never optimizable’

thheller21:10:23

yeah that I knew

thheller21:10:50

but the eval’d code should still be optimizable

mhuebert21:10:52

but that the eval code itself is also not optimizable, is a separate thing

thheller21:10:10

its super weird

thheller21:10:38

also very hard to google properly

thheller21:10:47

too many “eval is evil” results