Fork me on GitHub

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: 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 ( This seems similar.


Thanks @mfikes I will create a JIRA ticket as suggested.

👍 4

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?


I would prefer the CLJS version

👍 4

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 cljs


only :clj is in the reader macro


I've never seen this kind of issue. You may need to share a minimal repro.


phew, i’d be happy to but it could be significant effort


since i don’t actually know why this happens


Right. Is there more to the stack trace indicating where the constant is coming from?


i’ll try replacing all usage of my log.cljc with printlns, maybe that gets me somewhere. there’s no macros otherwise


yeah, it’s the ns form of the file, line 1


Can you share that form?


(it doesn’t say line 1, but it’s the only occurence of the namespace)


>>> (ns (:require [clojure.spec.alpha :as s] [com.stuartsierra.component :as component] [framework-core.bootstrap :as boot] [framework-core.error :as error] [ :as io] [framework-core.ioc :as ioc] [framework-core.log :as log]))


I can't see anything wrong with that.


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?


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.


going with React also means leveraging that ecosystem


if you don’t need it - no big deal


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.. 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


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


or rather, equality tries to check identical?, then falls back on equality


^(source code of equality in cljs)


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"


identity checking is cheap in every language!


> 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


huh. that's fascinating. yeah, that makes sense actually!


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




Having used React and redux ClojureScript wrappers around react make updating your state so much nicer.


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.

Garrett Hopper16:07:36

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.


jQuery components? Backbone components? Closure UI Components


I don’t think you’d want to use that stuff for the most part


Reagent doesn’t really succeed at hiding React


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.


but I don’t think they’ll necessarily be any better


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?


in the end yes you can wire these mutable JS UI libraries together


but they don’t really jive that well with ClojureScript for the obvious reasons


Well we are still talking about declarative/functional views, so we are not slipping into mutation Hell or amything.


I don’t see you can avoid that with traditional UI components


at least not without a lot of meaningless work anyway


VDOM conceptually is just computer graphics stuff - I don’t see what the problem is


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.


ok then sure - if you want to do your own thing - then of course - what’s the problem


VDOM is a kludge needed to create the illusion of functional immutable declarative etc. And it costs.




in about the same way computer science


the cost model isn’t even that interesting


you can always layer better reconciliation


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.


I would say >70% are using Reagent at this point, maybe even >80%


but people are writing new stuff all the time


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


Reagent/React is not that problematic and you have platform effects


the downsides really aren’t that big or or that interesting


Oh, wow, I heard re-frame was the go-to CLJS solution. Good to know.


my primary complaint about React/Reagent has nothing to do it per se


rather JS build tools kinda stink


Right, I am offering an upside.


and that holds true React or no


Don’t get me started on tooling!


I mean specifically long dep chains + lack of tree shaking


less about some perceived UX around the tools


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.

Jp Soares19:07:50

Is anyone using ? 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?