This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-07-18
Channels
- # aleph (4)
- # beginners (70)
- # cider (66)
- # clara (16)
- # cljdoc (20)
- # cljs-dev (9)
- # cljsrn (2)
- # clojure (36)
- # clojure-ecuador (2)
- # clojure-italy (14)
- # clojure-japan (2)
- # clojure-nl (22)
- # clojure-uk (79)
- # clojurescript (133)
- # clojutre (2)
- # code-reviews (5)
- # cursive (5)
- # data-science (1)
- # datomic (47)
- # duct (2)
- # emacs (1)
- # figwheel-main (3)
- # fulcro (11)
- # funcool (1)
- # graphql (6)
- # hyperfiddle (4)
- # leiningen (4)
- # luminus (9)
- # lumo (8)
- # mount (4)
- # nrepl (2)
- # off-topic (19)
- # onyx (1)
- # re-frame (23)
- # reagent (91)
- # reitit (17)
- # ring-swagger (2)
- # shadow-cljs (43)
- # tools-deps (27)
- # vim (45)
nope, some aspects of the worker API are sufficiently different that it's failing silently
@lee.justin.m heh redirecting 404 to index was a bad hack that broke a lot of relative links for some reason
Oh yea I wasn’t suggesting you actually do that 🙂 Just linked to it as evidence that you can’t do what you want to do on github pages
Hi,
I just came across some strange behaviour that I can't explain, regarding the use of the not
function in ClojureScript.
Using a ClojureScript (1.9.946) REPL If I run:
[(true? ((comp not empty?) "foo"))
(false? ((comp not empty?) ""))
(true? ((not empty?) "foo"))
(false? ((not empty?) ""))]
I get the result: [true true true true]
I would have expected this code to raise an exception. The first two expressions are valid, however the last two expressions don't use comp
and just apply not
to empty?
, which returns false
. I would expect this to break as false
is not a function (to my knowledge) and thus can't be applied to a string.
Interestingly if I run this code in a Clojure (not ClojureScript) REPL, I get an error that I would expect:
java.lang.Boolean cannot be cast to clojure.lang.IFn
If I compile this code with the ClojureScript compiler using advanced optimizations then I get a runtime error,
which is more like what I would expect:
Uncaught TypeError: b.call is not a function
Why does ((not empty?) "foo")
not raise an exception when compiled with ClojureScript using no optimizations?@mf this is indeed interesting
((not empty?) "foo")
emits this JS code !(cljs.core.empty_QMARK_).call(null,"foo");
which is not corresponding to CLJS code
it should be (!cljs.core.empty_QMARK_).call(null,"foo");
instead
@roman01la ok that's interesting. Presumably the JS code that is emitted when optimizations are turned on, is valid
Yeah @mf, probably worth a JIRA. We've had bad code gen related to missing parens before (https://dev.clojure.org/jira/browse/CLJS-2585). This seems similar.
Looks like ReactJS’s wheels are coming off. https://reactjs.org/blog/2018/03/29/react-v-16-3.html
OK, here is a strange situation. I developed a big reactive library in Common Lisp and ported it to CLJ as an exercise, then noticed CLJC and targeted that to get it on the client, and all that was so easy (week or two) that I said, what the heck, and ported it to JS. But, hang on! Now a CLJS version could be had by using interop with the JS version! So which would be more appealing to a CLJSer? A native CLJS version, not insignificantly guaranteed identical to the CLJ version, or a JS interop wrapper of almost exactly the same thing?
the file in question, checkouts/framework-core/src/cljc/framework_core/app.cljc
is obviously a cljc
file. i’ve checked it ten times now for weird reader conditionals or anything that might be causing issues like macros or whatever, it seems to be fine (like i said, clojure/jvm side works)
using lein cljsbuild once app
(clojurescript version 1.10.339
) in the example project produces the error i posted. does anyone have any pointer at all as to how to debug this further?
Do you have any macro declared on these cljc
files?
macros that would be used both in clojure and in clojurescript?
no, in app.cljc there’s none, but it requires log.cljc containing logger macros, but those are all wrapped in #?(:clj …)
Macros run in Clojure, so it'll run the :clj
reader path, not the :cljs
Maybe this can be your issue?
the macros are not defined for
only :clj
is in the reader macro
phew, i’d be happy to but it could be significant effort
since i don’t actually know why this happens
i’ll try replacing all usage of my log.cljc with println
s, maybe that gets me somewhere. there’s no macros otherwise
yeah, it’s the ns
form of the file, line 1
(it doesn’t say line 1, but it’s the only occurence of the namespace)
yeah sec
>>> (ns framework-core.app (:require [clojure.spec.alpha :as s] [com.stuartsierra.component :as component] [framework-core.bootstrap :as boot] [framework-core.error :as error] [http://framework-core.io :as io] [framework-core.ioc :as ioc] [framework-core.log :as log]))
yah mate the struggle is real with this one. i’ll see if the log cljc messes anything up
and since we’re there, can macros be defined in cljc files using #?(:clj ...)
?
would it help you if i posted the full stacktrace?
https://pastebin.com/TW2HKkvv here it is anyway 😉
For me, it's seems that somehow, the clojure.lang.Namespace
is leaking into Javascript implementation.
Try to comment each import on your file and check where the problem occurs. Then, check if that namespace that you imported is not bringing framework-core/app
into the project.
Probably framework_core/app.cljc
have some kind of macro or other construction that forces CLJS to bring Namespace
to your compilation...
Trust me, work with cljc
with ClojureScript is a pain... sometimes it's better to avoid it at all. I also have a similiar problem in my test library check
, and I'm almost quitting and creating two namespaces for the macros.
i have it resolved. it was actually some issue in another namespace. i removed my logger wrapper macros and replaced everything with printlns to test it, and it worked
the only macros there were the usual wrappers for ns capture. still an odd error.
star_ns_star that is 😋
I think the CLJS community fell in love with ReactJS for two reasons. First, the declarative/functional paradigm of components. I believe @dnolen called that out as huge (and it is wonderful). Second, and this is just a guess, the immutable state of CLJS was a perfect fit for knowing when not to update. To a degree, that let CLJS wrappers of ReactJS cure its original sin, viz., dissing the importance of managing state. R/atom especially delivered a superb reactive/dataflow implementation much like CLJS Javelin, JS MobX, and binding.scala, all of them simpler and more powerful than Flux/Redux (which seems already obsolescent thanks to Relay/GraphQL). So the question is…
If you still had declarative/functional views, would you miss ReactJS in a new CLJS web framework? I am trying to decide if I should toe the Facebook line or carry on with my thin HTML/CSS layer.
before discovering reagent i’ve written some dom stuff using pure cljs - it’s not bad actually even compared to something react-ish, coming from the good ol’ jquery days. actually a great way to get familiar with cljs as well. with react/reagent, the whole experience is so non-intrusive and makes the dom stuff just go away basically. with anything more advanced than the bare basics it was much easier to port it to reagent than to reinvent the wheel 🙂
so, this article is telling me that equality checking on large data structures is something to keep an eye out for in re-frame performance.. https://github.com/Day8/re-frame/blob/master/docs/Performance-Problems.md#2--on-big-structures and it makes me wonder exactly how equality checking works in clojurescript. I was under the impression that a deep walk of the data wasn't required for immutable data, so how exactly does the degree of nesting of a data structure contribute to the time complexity of an equality check?
side note, I think the example they use is kind of bad, because the example is nonperformant because any change in game-state data triggers a rerender for all components. The equality checking doesn't seem to factor into why that example is bad
The article explains that there are certain pathological cases where =
can take a while to return false
. And that this can combine badly with circumstances where a lot of unnecessary =
checking is done repeatedly.
This is an article about what can go wrong, right?
The issue is one of checking equality vs finding the difference of two data structures. Checking if two data structures are equal is a pointer comparison in ClojureScript. If the data structures are not equal then you'll need to do a deep walk to find where the data structures differ.
In the Reagent article you linked to finding out that two different 19 x 19 go boards are different is a fast check. Finding where they differ to render the change is where the equality check expense comes in.
It's the difference between =
and clojure.data.diff
hmm. see, when Iooked at the implementation of =
it seemed to check identical?
-- which would not return a true value for two different but equal values
and my question is "what is that fallback doing?" https://github.com/clojure/clojurescript/blob/1b7390450243693d0b24e8d3ad085c6da4eef204/src/main/cljs/cljs/core.cljs#L1108-L1124
Ah I see, if identical?
fails then a deep walk occurs using sequences and equality checks.
For example (list 1 2 3)
[1 2 3]` are equal but require using ClojureScript's seq
and =
abstractions to confirm this.
really? that seems crazy to me! because "in clojurescript identity checking is cheap and equality checking requires a deep walk" seems like a vastly different statement than "equality checking is cheap in clojurescript"
> Checking if two data structures are equal is a pointer comparison in ClojureScript.
@spinningtopsofdoom Not always! For instance (= (range n) (range n))
will operate in linear time. Immutability does not guarantee that equality is always a pointer comparison; however, it enables us to infer from pointer equality that nothing changed. With mutable data structures, we have to be more pessimistic, which is what makes change detection usually more costly.
so, I'm confused, because
(let [a [1 2]
b [1 2]]
(identical? a b))
=> false
so how does this pointer check come into play?@idiomancy in this case, it doesn't
but consider this:
(let [ns (range 1000000)
a {:numbers ns}
b {:numbers ns}]
(= a b))
this will be very fast, because even though a and b are not identical?
, their content is
Consider this case
(defn do-something [a] a)
(let [a [1 2]
b (do-something a)]
(identical? a b))
=> true
hmmm, interesting. So it's only going to deep walk until it finds an identical structure
do-something
could easily have return a different data structure. If we were using mutable data structures then we'd have to do a deep walk any time a potential change happened
okay so low level, what would trigger a pointer change?
it seems like the worst case scenario is in a large nested data structure there is a change at the leaf level
which would require a full walk in order to determine identity
I guess another way of asking this is
(let [a {:a 1}
b (assoc a :a 1])
[(identical? a b) (= a b)]
=> [false true]
I don't understand why a walk is ever necessary ... ohhhh
omg re-frame using =
is horrible
... or is it
:thinking_face:
Having used React and redux ClojureScript wrappers around react make updating your state so much nicer.
indeed
no the =
behavior makes perfect sense
just need to understand it
You end up having to do what =
does under then hood but customized for every single change. Add making sure your updates don't introduce mutable shared state and well...
that go-board example is an interesting one. i guess the point here is that identical?
can tell you whether structures are definitely the same, but it can’t tell you whether they are definitely different. because in that example there were 361 components, all of which received different datastructures, that requires a deep equality check to make sure. it probably would have been faster in that case to use identical?
and just rerender the components given that they are pure (obviously you don’t do that, but it’s an interesting thought experiment). the example is an interesting case where the work in the vdom is more expensive than what you save through reconciliation
@dnolen Yes, the React ecosystem is a big consideration. But there was a lot of code written for HTML/CS/JS before ReactJS, so there may even be an advantage to going “native”. My other thought is that a code base targeting React via sth. like reagent or re-frame might be easier to extend to ReactNative, which AirBnB aside seems the preferred route to mobile. But if a new framework works out, methinks a bridge to RN could hide the RN lifecycle as reagent does and the bulk of the code base would work unchanged. My unrelated thought is that VDOM was a mistake, why perpetuate it?
One reason to perpetuate it is that you are arguably in the minority view. 🙂 The VDOM is great and makes it easy to write correct code. It is, imo, a much more natural fit for functional programming than doing some kind of observer model thing where you mutate the dom directly.
I have a :foreign-libs
library for a Google API. gapi
is the :global-exports
, and I put it under the google.api
namespace.
The problem is that I need to access static functions under gapi
, and I can't seem to find any way to do it.
1. I can't import google.api.auth2
(I didn't export that namespace, so this makes sense.)
2. I can't just use google.api.auth2/$staticFn
3. Using (.staticFn (.-auth2 google.api))
runs into problems, because it's not treated like a static function using the $ns/$fn
syntax as far as I can tell.
@hiskennyness what code?
All the above?
With a thin wrapper any existing code should drop in, or port mechanically.
you could use that stuff but I don’t know what value you’d be getting out of it - they won’t go away because of legacy maintenance
qooxdoo did a lot of nice work.
No, but we are not in a desert of an ecosystem is all I am saying.
I’ve been doing JS since the 2005, I don’t have anything good to say about qooxdoo or these other rich UI libs - your mileage may vary
So who else besides React has done good work?
Well we are still talking about declarative/functional views, so we are not slipping into mutation Hell or amything.
What I did was create a truly reactive layer with glue that drove qooxdoo. So deep down there is mutation (just like React once it has diffed the vdom, but the library user never goes there.
VDOM is a kludge needed to create the illusion of functional immutable declarative etc. And it costs.
Well I am talking about a new CLJS framework, so I am trying to decide if I should wrap React or if the community is committed in some way to React.
Well, the cost drove them to include (I forget) “dontUpdateMe” in the lifecycle forcing the user to decide that question with their own state management, so I think what happened was it did not scale.
but I don’t think you’re going to convince large number of people to use some other thing
Oh, wow, I heard re-frame was the go-to CLJS solution. Good to know.
Right, I am offering an upside.
Don’t get me started on tooling!
I think the only tooling I want is good ol’ CLJS->JS.
@hiskennyness re your comment above: re-frame is built on top of reagent
Thanks, @lee.justin.m I know. And yes, I imagine the 70-80% @dnolen cited included re-framers. Makes more sense now.
Is anyone using http://www.webcomponents.org ? I tested importing one component and it seems that different components might need different ways to import. Are there some known issues or tips for using them with re-frame?