This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-03-11
Channels
- # aleph (1)
- # beginners (192)
- # boot (9)
- # cider (12)
- # cljs-dev (223)
- # clojure (30)
- # clojure-brasil (3)
- # clojure-china (1)
- # clojure-italy (4)
- # clojure-russia (24)
- # clojure-spec (7)
- # clojured (5)
- # clojurescript (82)
- # cursive (9)
- # datomic (24)
- # emacs (6)
- # fulcro (9)
- # keechma (1)
- # luminus (1)
- # off-topic (3)
- # om-next (5)
- # onyx (1)
- # parinfer (9)
- # re-frame (1)
- # reitit (4)
- # ring-swagger (6)
- # shadow-cljs (8)
- # spacemacs (1)
- # sql (1)
- # unrepl (13)
@dnolen @anmonteiro It works! This is amazing 🙂 https://gist.github.com/Jannis/c101329b80bbc6a498129af3e702b3f8
(One error still (even though it carries on) but that’s a circular dep inside graphql-js, which will be fixed in the next release.)
Had a little fun with an experiment for warnings on private var use
cljs.user=> (def ^:private a 3)
#'cljs.user/a
cljs.user=> a
3
cljs.user=> (ns foo.core)
nil
foo.core=> cljs.user/a
WARNING: Use of private Var cljs.user/a at line 1 <cljs repl>
3
foo.core=> @#'cljs.user/a
3
@dnolen Right, the problem is however that the MetaFn
is an (ugly) hack that breaks down. For instance when we have a defn
like this:
(defn foo [a b & c] ...)
;; and call it like this:
(foo 1 2 3 4)
;; CLJS will generate code for static-fns like this:
the.ns.foo.cljs$core$IFn$_invoke$arity$variadic((1),(2),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([(3),(4)], 0))
This however will be a complete mismatch to the new IFn$_invoke$arity$variadic
that got generated by the (fn [& args])
. E.g. this fails:
(defn ^:dynamic *foo*
[a b & c]
[a b c])
(MD/ensure-static-fns! true)
(defn call-variadic []
(*foo* 1 2 3 4))
(MD/restore-static-fns!)
(let [orig *foo*]
(binding [*foo* (fn [& args]
(apply orig args))]
(call-variadic)))
Where (MD/ensure-static-fns! true)
is just a macro mutating the compiler (set! ana/*cljs-static-fns* x)
Though, this has nothing to do with 2641 and is separate bug. The problem with 2641 isn't dynamic binding but the fact that the dispatchers and the variadic impls will refer to the namespace object which will be the new (wrapped) impl of spec. Thus the stack overflow
Check the implementations for top level defn's:
(defn bar [& a] ())
(defn print-fn
[f]
(into {"dispatcher!!" (str f)}
(map #(-> [% (str (aget f %))]))
(seq (js-keys f))))
(print-fn bar)
And unfortunately the problem is that there seems to be no easy way to fix this. One solution is to write an apply that avoids the applyTo call and goes straight to all actual impls. FWIW, I've advocated for such a centralized applyTo
last year. But I'm still waiting on the IFn refactorings and apply test-suit to be merged.
Wow, this is amazing. I didn't know you can have a git dep :sha
pointing at a commit in a branch. You can trivially share an experimental change to the compiler to get feedback: https://gist.github.com/mfikes/e05fc6003f24b6565540d42f1c9dbbae
I was thinking a bit about the “runtime config” idea[1], one use case I had in some of my libraries was that based on external configuration my library would elide some code (e.g. elide logging or some diagnostics checks[2]). First, I thought it would not be possible to do that anymore, because it was done in macros at compile-time (based on configuration a macro would emit different code). But then I realised that the main use case for such optimizations was :advanced mode. It turns out I can still use :closure-defines
and in advanced mode rely on google closure compiler to DCE code which doesn’t get called (thanks to the fact that :closure-defines
gets baked-in as a static code in advanced mode). It is much more brittle but doable I guess. But still I’m thinking about something like “compile-time config” which would be available to macros. And clojurescript compiler would make sure anytime this compile-time config changes, AOT caches get invalidated. Sometimes it would be much more convenient to rely on macros to generate config-dependent code than to rely on google closure compiler. Any opinions? I saw David being strongly against blowing caches up, but I think this would be a rare thing and expectable to recompile whole project when compile-time config changes. Should I open a JIRA ticket for more persistent discussion about this? @bhauman @dnolen
[1] https://github.com/darwin/clojurescript/compare/92cbedd12cd39d6bc3083a269a10fba9daa7fc40...runtime-config
[2] https://github.com/binaryage/chromex/blob/master/src/lib/chromex/config.clj#L9
I’m not familiar with how AOT caches work in java/clojure but I can imagine that clojurescript could control cache directory based on compile-time config (e.g. hashing it and using this hash as a sub-folder). This way each compile-time config variant would get its own cachedir. It would not mean blowing up world cache for this to work. But maybe this is just a naive thinking on my part.
@rauh I came to the same conclusions yesterday - spec instrumenting is just showing an issue that has been true for quite some time
the root of the issue is that we changed how top level defns work to support cross module code motion
@dnolen do you have some link where I could learn more about this to understand how it works? you said that relying on :external-config
in compiler opts in my macros won’t work anymore, also I guess there is a lot of code which checks :advanced mode compiler option in a macro and does something different based on that, is it true, that this code won’t work anymore, because :advanced mode option is not part of cache-invalidation logic?
people need to start realizing this is an anti-pattern, and the popular libs just need to stop doing it - full stop
@darwin however … libs can at macro time depend on cljs.analyzer/build-affecting-options
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer.cljc#L3837
@dnolen I see, so you have this mechanism already in place, would you be against adding another key into build-affecting-options? lib authors could safely rely on that configuration at macro-time (e.g. that :external-config
me and @bhauman and maybe few others started using without you knowing[1]). There must be a way for me to elide some code at macro-time in a lib based on user-specified config.
[1] https://github.com/bhauman/lein-figwheel/blob/master/support/src/figwheel/env_config.clj#L15-L16
How expensive would it be to just macroexpand the entire ns and then check if anything changed? That would solve all issues, though would need to be smart about all the gensyms
... Just a thinking out loud
e.g. cljs-oops would be busted if it cannot do something like this at macro-time, it generates very different code under :advanced, see [1] and [2] [1] https://github.com/binaryage/cljs-oops/blob/master/test/transcripts/expected/ocall_dev.js [2] https://github.com/binaryage/cljs-oops/blob/master/test/transcripts/expected/ocall_static_core.js
everybody has to understand that caching is a global service, and ClojureScript controls it
(also or they decide not to … it’s unlikely that :aot-cache
will ever become the default given the state of things)
@rauh re: spec I sorted out a clever way to get out of the issue for now - since we bind f
to itself via .call
in the apply helpers we check that in the dispatch and choose that instead so we get the original properties and not the overriden ones
@darwin, actually cljs-oops is probably fine if you’re just checking advanced - while optimization level isn’t a cache key - we change other things that are
ok, that is good news, but still cljs-oops is currently more flexible than that, user can enable diagnostics in :advanced mode, yes, default behaviour was enabled in dev-time, disabled in advanced-mode, but technically there is no restriction to enable/disable it in either mode
yes if you’re doing more configuration via macros that will change compile output - of course that won’t work
@rauh https://github.com/clojure/clojurescript/commit/9dd4d41174079a568f27dcdf061568c985b2bd12
@dnolen Why do we need the check in applyTo
? Shouldn't this always be the fn? applyTo
in only ever used in apply
, nowhere else IIRC
you always need the check because in the generated code we go back through the namespace
Also I still think the MetaFn
hack needs to go, the issue I described first (`variadic`) is still present. So there def needs to be a change in that spec function
@dnolen I dont understand that, unless somebody writes (let [f (.-cljs$lang$applyTo some-fn)] (f ...))
we always have this bound to the fn and the check will always go to your new way of invoking it.
@rauh I don’t think this point is really that important anyway - it didn’t work for me w/o but you can play around with it some more if you like
It's identical to https://dev.clojure.org/jira/browse/CLJS-1663, but instead of the compiler emitting f.cljs$core$IFn$_invoke$arity$1(...)
it's for foo.cljs$core$IFn$_invoke$arity$variadic((1),(2),cljs.core.prim_seq.cljs$core$IFn$_invoke$arity$2([(3),(4)], 0))
if the cljs.compiler works with options that don't include a :main and it also supports code splitting which doesn't have a main, and figwheel works in these situations as well, then its not accurate to say that I can simple use closure-defines to propagate config @dnolen @darwin
@rauh MetaFn
bit isn’t going to change unless you come up with an acceptable alternative for advanced compilation
This will throw now since the wrapper only defined foo.cljs$core$IFn$_invoke$arity$variadic( ONE_SINGLE_ARG )
it is accurate to say that artifacts that change based on the environment are probably a bad idea
I'm saying that closure-defines don't appear to be a silver bullet to solve my problem
I'll give it a try again, but when I've used them in the past it seemed spotty at best
BTW I also had a version of figwheel that tried to use code as an input source to propagate config
@rauh anyways, just come up with something that isn’t working and make an issue - it’s too hard to understand what you are saying otherwise
@bhauman Closure defines have been there a long time - one problem in the past was people didn’t know how to make it worked uncompiled, but that’s not true anymore
@dnolen I mean there is more issues with such a wrapper. Eg. some people might add other props to a function. (Like we do for variadic fns). I'd suggest: Loop over all IFn
and the variadic
(those will have implementations) and wrap them individually. Much more robust.
@dnolen With CLJS-2646, the fact that it the default suggested has "/main.js" seemed like it might be an issue, if the user simply copies that template as is. I suppose things can work that way, but essentially the 3rd patch in that ticket tries to make it so that you have a relative path to main.js
. It honestly turns into a bit of a hack.
@dnolen Arguably nothing is horribly broken as is. Things work. Perhaps I wrote the ticket out of aesthetics.
well, my problem probably was that when I wanted to use :closure-defines it usually was for optional code elision, and that would not work without dark-magic like adding ^boolean type hints to my code for closure compiler to recognise it, sometimes when I rewrote it to something more complex, DCE again stopped working because it was too complex for closure compiler to understand, in the light of these issue using a macro for generating only the code needed was a clear win
@mfikes yeah I think as long as we do the temp dir thing I think what it shows makes sense
OK, yeah. (I'm never done much webdev, so the absolute path looked odd to me, but I can see that it could be viewed as normal; the a path starting from the root of where things are being served.)
@darwin that’s just end user trouble shooting - so again it’s just about ease and convenience - which sent everybody down the wrong path in the long term
@rauh I don’t think you can loop over IFn
and the other properties as strings, not advanced compilation safe - you’d have to write something that unrolls into static operations
@dnolen Right, I didnt mean "loop" int he literal sense. I'd suggest a macro looping over all IFn & variadic
A cleaner solution IMO would be: Don't (set! ...)
the given var-fn at all. Instead: Save the original fn as f.specImplFn = f
, then a f = spec_checking_fn_which_calls_specImplFn_on_itself
. Could even use a nice protocol for it.
Anyways, I guess unless user have issues with it... Nothing more todo. Would be a lot of work 🙂
Yeah agree, though it could be cool.. You could spec data-types that way on their IFns 🙂
@mfikes re: the path thing, Quick Start covers -d out
so hopefully that gets people doing that most of the time
Yeah, this whole thing is an enabler, which we are only starting to scratch the surface with 🙂
@juhoteperi https://dev.clojure.org/jira/browse/CLJS-2633, is this supposed to work anymore w/o specifying :npm-deps
?
@dnolen Yes. I'm using 1.10.126 without :npm-deps
, just package.json
and node_modules
seems something wrong with the js-dependency-index - some of the processed files don’t even show up in cljs_deps.js
@juhoteperi does Google Closure support scoped requires?
@material
thing? Not sure
Issue says this would be regression
But yeah, would be my guess this has something to do with that
does Closure support requires like react/server
? because scoped requires are the same thing
For some reason Closure CompilerInput .getRequires returns those "strange" names for snackbar/index.js requires
hrm I don’t see how this ever worked, unless Closure supported it before and dropped it somehow
I think all the files are correctly processed etc. Closure just provides us with bad dependency information for some reason
Previously Closure added the goog.require calls itself, now we add those ourselves based on Closure information, which is not necessarily the same information as used by Closure previously
@juhoteperi so you’re saying result of getRequires
for some of the requires are bad?
@juhoteperi https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/deps/ModuleNames.java#L63
Not sure if @
causes problems in some later stage, but it shouldn't break .getRequires
Looks to me that @
should work, cljs_deps.js
and everything looks correct for the modules that get included
It's just that snackbar/index requires module$$material$base$index
instead of module$home$juho$tmp$string_requires_npm_deps$node_modules$$material$snackbar$node_modules$$material$base$index
which would exist
I don't have more time to look into this now, but the easiest place to debug this is: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/closure.clj#L1761 and debug-prn
or something module-name
and (vec (.getRequires input))
@juhoteperi yeah was about to fire up IntelliJ debugger and start stepping
@juhoteperi thanks for the info so far, still helpful
The DependecyInfo is populated during either of these .process calls: https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/closure.clj#L1825-L1826
I did quick check with closure-compiler master, and that didn't change this
sorry to report a degradation of browser-repl behavior in terms of a connection surviving a browser refresh
also clj -m cljs.main -O simple -c example.core
emits WARNING: :preloads should only be specified with :none optimizations
and :browser-repl true
in the cached opts
I'm doing these explorations to inform the design of figwheel so that it will work in simple and whitespace
@bhauman oh cool so you’re just running through all the different options via cljs.main
yep I was wondering if certain things got skipped in those modes that affect reload and such, but again easy to check out now