This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-06-27
Channels
- # bangalore-clj (2)
- # beginners (37)
- # boot (16)
- # cider (17)
- # clara (4)
- # cljs-dev (351)
- # cljsrn (16)
- # clojure (219)
- # clojure-belgium (4)
- # clojure-dev (3)
- # clojure-france (2)
- # clojure-italy (24)
- # clojure-russia (23)
- # clojure-spec (55)
- # clojure-switzerland (3)
- # clojure-uk (89)
- # clojurescript (121)
- # cursive (2)
- # datomic (29)
- # devops (2)
- # graphql (8)
- # hoplon (15)
- # immutant (5)
- # lein-figwheel (4)
- # liberator (3)
- # luminus (18)
- # off-topic (9)
- # om (6)
- # onyx (31)
- # pedestal (48)
- # precept (9)
- # re-frame (19)
- # reagent (63)
- # ring-swagger (69)
- # robots (1)
- # slack-help (14)
- # spacemacs (12)
- # sql (2)
- # test-check (4)
- # unrepl (28)
- # untangled (5)
- # yada (3)
@anmonteiro thanks applied
ok I have the mailing list post prepped but I want to post it after the release blog post (tomorrow)
These site PRs can be pulled
https://github.com/clojure/clojurescript-site/pull/88 (Differences page, ^:const
)
https://github.com/clojure/clojurescript-site/pull/89 (Updates compiler warnings list)
This isn't supposed to work, right:
IFn
(-invoke [_ & a]
(or p (set! p (apply clojure.core/partial f args)))
(apply p a))
@juhoteperi have you seen this? It looks like 1.9.655 breaks reagent.
I can't claim to understand what partial-ifn does
Only with :static-fns true
afaik
Work for me, and I'm not using that option
I though static-fns is enabled by default in the latest release?
It defaults to true with :optimizations :advanced
, I haven't tested that yet
(defn partial
"Works just like clojure.core/partial, except that it is an IFn, and
the result can be compared with ="
[f & args]
(util/partial-ifn. f args nil))
what are the options? Just thinking out loud 1. find a different implementation of partial-ifn and release a patched version of reagent 2. tell people to manually set static-fns to false when using advanced optimization for now 3. modify the clojurescript compiler to allow this 4. revert the default to :static-fns true until this is fixed
The -equiv
is the important part, it is used at least by reagent/wrap
and partial
is also part of Reagent public API.
@pesterhazy I'm 99% sure it'll be option 1. Variadic protocols have never officially been supported in CLJ nor CLJS.
maybe so, still many, many clojurescript projects use reagent
so whatever the merits of this change, it will effectively break a good percentage of production builds out there
static-fns true for adv. has been default for at least 2 years
As soon as we have fix for Reagent, I can do a new release
is the "ifn" part even still valid?
cljs.user=> (def prn* (partial prn :foo))
#'cljs.user/prn*
cljs.user=> (ifn? prn)
true
cljs.user=> (ifn? prn*)
true
Normal partial
is Fn which means if also implements IFn, Reagent partia is IFn but not Fn
I'm guessing there was a reason for this name "partial-ifn" at some point - maybe partials didn't satisfy IFn with earlier versions of clojurescript?
@rauh, could you link to the patch you mentioned?
IFn part is not important
@pesterhazy @juhoteperi to clarify, it used to be only broken with static-fns true, with 1.9.655 it is now also broken with static-fns false and with the defaults for opt. :none
@juhoteperi FWIW, if you're trying to find an easy fix: You can use MetaFn
and store the f
and args
in the meta data and specify!
your IEquiv
. That could work.
reagent uses a lot of magic unfortunately
Is MetaFn same as with-meta
for a fn?
@kommen oh, so it affects all reagent users? that makes the problem more critical
Oh, reloaded the app and it broke, must have been running cached version
@juhoteperi Like this:
(defn partial-fn
[f & args]
(let [pfn (with-meta (apply partial f args) [f args])]
(specify! pfn
IEquiv
(-equiv [f other]
(and (instance? MetaFn other)
(= (.-meta f) (meta other)))))
pfn))
(= (partial-fn prn "foo") (partial-fn prn "foo"))
@rauh That looks good, thanks
@pesterhazy it looks so to me, but only for not so common use cases
I'll write some tests for this and try the change
@kommen not sure this is uncommon - partial-ifn is used for r/wrap as well
@pesterhazy ok. i assumed more people would have ran into it with static-fns true
So is the original problem using variadic arity for invoke? And to properly implement IFn it would have to declare ~20 arities?
A minor problem with using meta is that it will break if user uses with-meta
for partial
result
@juhoteperi FWIW, this matches your assertion. Core does this for the Var
deftype
https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/core.cljs#L1047
@juhoteperi You didn't support IMeta
in your current impl, so that woudln't break anything. But you'd have to be careful when a user passes in a MetaFn
already, then you'd have to carefully funnel the original meta data in and out. Probably better to do a new clean deftype.
Yes, doing that now
looking back over this now - looks like Reagent assumed variadic protocols were a thing? Which they aren’t
@dnolen See the 1. 2. 3. 4.
list in the ticket: https://dev.clojure.org/jira/browse/CLJS-2099
Once we're out of args (`next` is nil) we either do f.IFn$invoke$arity$X
or f.call(f, ....)
Reagent used foo.
and ->foo
, not sure what those compile to
@juhoteperi ok then I don’t see what @rauh means wrt. to this statement
I think that was a mistake. I'm confused now too 😄. I'm checking the generated source rihgt now to make sense of it.
Any way, Reagent code was a mess, but this will fix it: https://github.com/reagent-project/reagent/compare/fix-partial-ifn?expand=1
Could be, but I'm not sure how often this is really used
The old way to call partial the first time invoke was called was quite... confusing..
I think this might be it: Previously it got passed in this as the first argument:
partial_ifn.prototype.apply = (function (self__,args100343){
var self__ = this;
var self____$1 = this;
return self____$1.call.apply(self____$1,[self____$1].concat(cljs.core.aclone.call(null,args100343)));
});
The .call
then went to the delegate, wich properly calls the variadic
function. But some of the code doesn't use the reassigned this
but instead the passed self__
parameter (first param):
var G__100344__delegate = function (self__,a){
var self____$1 = this;
var _ = self____$1;
var or__31642__auto___100345 = self__.p;
if(cljs.core.truth_(or__31642__auto___100345)){
} else {
self__.p = cljs.core.apply.call(null,cljs.core.partial,self__.f,self__.args);
}
Which we can't provide anymore now with the new apply
implementation. All this .call
and .apply
on the prototype is really obsolete nowadays.
FWIW, I don't get a warning when doing this. So it looks like https://dev.clojure.org/jira/browse/CLJS-1445 regressed.
we don’t check implementers because assumed that people would look at the protocol when implementing
@juhoteperi so can a Reagent release coincide with 1.9.655 announce?
Sure, just merged and pushed a snapshot
@pesterhazy Can you test 0.7.0-SNAPSHOT
I'll test this myself and hopefully someone else can confirm it works and then I'll do a proper release
but libs may have done believing it was a thing for IFn
specifically since covering all the arities is tedious
I don’t know why - but pointing out this anti-pattern will not work now that we’ve optimized invocation is important
@juhoteperi i can test it in 15 min
Good, works for me in dev build, with adv build I hit problems with schema-tools and default
field in a record
Drafting something along the lines of
> Note that, owing to optimizations in ClojureScript 1.9.655, incorrect code employing variadic signatures in protocol method implementations may fail. Be sure to update any code or libraries that make use of this construct.
@juhoteperi you mean Closure warnings for default
field?
Oh right
I mean the record with default
field is from Schema-tools
@mfikes I think we should add a line that protocol implementations must match some existing signature
> Note that, owing to optimizations in ClojureScript 1.9.655, incorrect code employing variadic signatures in protocol method implementations may fail. Be sure to update any code or libraries that make use of this construct so that protocol implementations match some existing signature.
Should Reagent be mentioned specifically?
I’m actually ok with supported the old busted behavior if it’s not terrible and emitting warning moving forward
This code is the problem, that's the arity2 protocol, that apply
now calls directly, note the .call
still works fine (I was wrong entirely earlier):
partial_ifn$$.prototype.$cljs$core$IFn$_invoke$arity$2$ = function() {
function $G__22894$$($G__22894$$) {
var $var_args$jscomp$432$$ = null;
if (0 < arguments.length) {
for (var $var_args$jscomp$432$$ = 0, $G__22895__a$$ = Array(arguments.length - 0); $var_args$jscomp$432$$ < $G__22895__a$$.length;) {
$G__22895__a$$[$var_args$jscomp$432$$] = arguments[$var_args$jscomp$432$$ + 0], ++$var_args$jscomp$432$$;
}
$var_args$jscomp$432$$ = new $cljs$core$IndexedSeq$$($G__22895__a$$, 0, null);
}
return $G__22894__delegate$$.call(this, $var_args$jscomp$432$$);
}
function $G__22894__delegate$$($G__22894$$) {
///////// PROBLEM HERE: THE __self:
return $cljs$core$apply$cljs$0core$0IFn$0_invoke$0arity$02$$($cljs$core$apply$cljs$0core$0IFn$0_invoke$0arity$03$$($cljs$core$partial$$, self__.$f$, self__.args), $G__22894$$);
}
$G__22894$$.$cljs$lang$maxFixedArity$ = 0;
$G__22894$$.$cljs$lang$applyTo$ = function($G__22894$$) {
$G__22894$$ = $cljs$core$seq$$($G__22894$$);
return $G__22894__delegate$$($G__22894$$);
};
$G__22894$$.$cljs$core$IFn$_invoke$arity$variadic$ = $G__22894__delegate$$;
return $G__22894$$;
}();
https://github.com/clojure/clojurescript-site/pull/90 has been updated
@juhoteperi if we can make it work for the time being no reason to call out Reagent in this post
1.9.655 won’t be the version though, if @rauh comes up with something, I want to include the warning patch.
Yes, the problem is the __self
, it's not set anywhere. Would mean messing with the compiler, not sure I could be of help on this one. 🙂
@dnolen Inserting this (emitln "var self__ = this;")
at line 751 on compiler.cljc should fix it
Yeah I uncommented that, but taht didn't seem to fix it. Though I may have missed something.
@juhoteperi looks good here as well
@juhoteperi trying your snapshot now
@juhoteperi @pesterhazy et al. please try Reagent with master
@dnolen, using the test case in https://dev.clojure.org/jira/browse/CLJS-2133: works in 1.9.562, fails in 1.9.655 and also fails in 1.9.656 (master)
655 gives Uncaught ReferenceError: self__ is not defined
656 gives
Uncaught TypeError: Cannot read property 'cljs$lang$applyTo' of undefined
at Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (core.cljs:3800)
at cljs$core$apply (core.cljs:3797)
at reagent.impl.util.partial_ifn.G__8391__delegate (util.cljs:65)
at reagent.impl.util.partial_ifn.G__8391 [as cljs$core$IFn$_invoke$arity$2] (util.cljs:63)
at Function.cljs.core.apply_to_simple.cljs$core$IFn$_invoke$arity$4 (core.cljs:3781)
at Function.cljs.core.apply_to_simple.cljs$core$IFn$_invoke$arity$3 (core.cljs:3777)
at Function.cljs.core.apply_to_simple.cljs$core$IFn$_invoke$arity$2 (core.cljs:3771)
at Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (core.cljs:3806)
at cljs$core$apply (core.cljs:3797)
at main.cljs:6
@pesterhazy k thanks that’s helpful
@dnolen, test case to reproduce https://github.com/pesterhazy/reagent-655 - you can specify the cljs compiler version like this: CLJS_VERSION=1.9.656 ./build
@pesterhazy if you can try master please
yup, 1.9.659 fixes it for me
magical 🙂
Great!
@mfikes @anmonteiro if you all have time to check out self host on your end that would be cool
Yes, Planck is passing its tests… I’ll try to see if the specific variadic thing in Reagent works
@dnolen many thanks for this deus ex machina fix
Is this a sufficient test?
cljs.user=> (defrecord Foo []
#_=> IFn
#_=> (-invoke [this & r]
#_=> (apply prn this r)))
cljs.user/Foo
cljs.user=> ((->Foo) 1 2 3)
#cljs.user.Foo{} 1 2 3
nil
I’m worried Reagent was doing something more than just variadic. But the above is in Planck.https://github.com/clojure/clojurescript/commit/809f23ae11f1a02c45a69e0cc9532b1709385066
@dnolen Looks good. Here is is in Planck build against latest master. https://gist.github.com/mfikes/3256f8a544923e7207349974274b988a Oddly, it also worked when built against 1.9.655. I included that as well in the gist (with full verbose output). I did get a
null is not an object (evaluating 'self__.f')
but only in the interim 1.9.657; ignoring that aspect.@mfikes if you want to fix up that post would be nice to just push this out the door now
@mfikes You'd need to all the arity-2 IFn
on the partial-fn
object to trigger the error. The new apply
will do that for you if you pass 2 arguments.
> Note that this release introduces a new warning for incorrect code which employs variadic signatures in protocol method implementations. Such code will continue to work with this release. Be sure to update any code or libraries that make use of this construct so that protocol implementations match some existing signature.
It looks like the test in core currently only tests the error that was introduced in the commit like 1h ago.
@dnolen i can confirm 1.9.660 works with the reagent 0.6.2 and prints the warning in our nextjournal code base
So thinking of this, those users that use variadic protocols can't use :fn-direct-invoke
.
Added this to https://github.com/clojure/clojurescript-site/pull/89
`:protocol-impl-with-variadic-method`, protocol impl employs variadic signature
There are 2 other PRs that are of lesser importantce, but also related to this release
@rauh Probably water under the bridge, but here is me trying to get it to fail with 1.9.655: https://gist.github.com/mfikes/0caea23d6a40953201f2917632a1e17c
@rauh Thanks. That caused it to fail properly with
Can't find variable: self__
cljs.core/apply (cljs/core.cljs:3797:1)
And to be sure, I re-tried the above in Planck against master and it works (while emitting the desired warning when I define the deftype
)
@dnolen Changed filename and date in file https://github.com/clojure/clojurescript-site/pull/92
Cool https://clojurescript.org/news/2017-06-27-faster-compilation-runtime-and-spec-caching-fixes is loading for me
I can add a link to https://github.com/clojure/clojurescript/blob/master/changes.md if that’s what you are suggesting. Perhaps with some appropriate text.
@dnolen made a small tweak to the release post, headings looked huge: https://github.com/clojure/clojurescript-site/pull/97/files
also I’m a bit late to the party but everything looks OK downstream in Lumo
@anmonteiro merged thanks
I finally updated this longstanding PR about updating the Babel transform example to use Cljsjs package, and now it also has another section with the long form: https://github.com/clojure/clojurescript-site/pull/54/files
In Boot-cljs I have implemented code (not yet released) to automatically try to require namespaces referred by namespaced :preprocess
values, and added a way to provide initialization code to evaluate before running Cljs compiler (as Boot-cljs runs Cljs compiler in separate classloader etc.)
Not sure what is the way to provide js-transforms
/ require namespace in cljsbuild / figwheel (when using lein plugin, and not from REPL)
@juhoteperi yeah I don’t have any answer for that other than that probably needs to be provided by a separate jar
I guess ClojureScript could consider loading these files directly for you a la data literals
Yes, if the convention is "low level enough" (doesn't depend on built tooling) I think it could make sense to implement it in ClojureScript instead of reimplementing it in every tool
I think there is no issue about this yet?
@juhoteperi there is not, go for it if you want to open it
why isn't that here? https://github.com/bhauman/lein-figwheel/wiki/Configuration-Options @bhauman
There are a lot of options: https://github.com/bhauman/lein-figwheel#more-figwheel-configuration-information
with 1.9.660
+ :static-fns
+ :fn-invoke-direct
, I see
(ns foo.bar)
(defn baz []
((if true :a :b) {}))
compiled to
// Compiled by ClojureScript 1.9.660 {:static-fns true, :fn-invoke-direct true}
goog.provide('foo.bar');
goog.require('cljs.core');
foo.bar.baz = (function foo$bar$baz(){
return new cljs.core.Keyword(null,"a","a",-2123407586)
(cljs.core.PersistentArrayMap.EMPTY);
});
//# sourceMappingURL=bar.js.map
which then fails at runtime with TypeError: (intermediate value) is not a function
@moxaj I’m not saying you are - just setting expectations. And I have no idea and I don’t have time to look at.
I would talk to @rauh and determine if there’s something to file when he has time for it.
you added a special case for the HO-invoke
stuff which checks for keywords but its dynamic in this case so it assumes to always get a function
@moxaj ok I took some time to look at it here - the code gen here is in fact just wrong
Yeah it looks like it doesn't even work with :optimize-constants
. I think we just need to let
the if
@rauh not the issue, you didn’t account for (thing {:a 0})
where thing is a keyword (that is unknown to the analyzer, ie. dynamic)
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer.cljc#L2903
Yeah I don't know enough about the analyzier here, but I'm guess that (if x :a :b)
will be :tag
'Keyword
, hence the binding won't kick in.
@thheller you need to understand how parse-invoke
works in this case (seperate from if
optimization)
https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/compiler.cljc#L1049-L1051
since we can’t know the type reliably in this case and the keyword?
case never triggers
As I see, there is mismatch, the emitter looks at (and (= (-> f :op) :constant) (keyword? (-> f :form)))
whereas the invoke parser looks at :tag
. So one of them should change. The emitter can append the IFn
protocol when the tag is keyword, or we bind the expression, even if it's a keyword tag. Or am I completely off?
@dnolen I think the final case in the emitter will never be used if we properly fix the invoke parser. It'll always go to the second to case in the emitter (checking IFn
).
@rauh ((read-string ":foo") ...)
you cannot always know the type, so there must be a fallback
If we consider (x a0)
what can be x
? Once we fix the parser (that possibly emits a let
) we're can have: fn? js? goog?, variadic-invoke, keyword?, proto? (= (:op f) :var)
. It can't be an expression anymore so the last case should never happen.
@dnolen This: keyword? (= 'cljs.core/Keyword (ana/infer-tag env f))
in the emitter fixes it.
@thheller the read-string
case you’re talking about is covered because that gets rewritten into a let
assigning the result of the expression to a local thus it becomes a var case which we can handle
@rauh if you fix this by changing the emitter, don't forget cljs.core/Symbol
(also callable but not a js-fn)
@favila we don’t need to account for that since we don’t account for it in parse-invoke
Or trigger an error, asking people to contact us? We eventually wanna remove all those .call
from the prototype IMO.
I mean it was a syntax error so it was spotted pretty quickly. IMO not to terrible given we're working with smart developers here.
@rauh one thing that’s really important to understand is that there’s 6 years of ClojureScript out there
Yeah understood. Given it's 99% never used in "normal" code, we could centralize the dispatchers (which would be slightly slower) and keep everything backwards compat.