react 2020-05-30

what do you mean 'scope'? is that the same as redux's store?

scope in js means 'access to names', that is, a namespace, is that the same thing in cljs?

There's a similarity with what I'm asking and js scope. I'm asking about which components define and manage which pieces of state. Reframe uses a global scope. Redux uses tree-local scope. Etc.

I earlier gave an analysis of this matter which I claim is complete, each react component have 3 distinct states in might handle. 1. state that is displayed, this includes hardcoded values like text or hiccup/jsx/html/element tagname/attr , but it also includes state that is derived from external sources but still has to be displayed in some manner 2. state that is necessary for its internal functioning, logic, business imperatives, but it is not something that is directly driving the output, so this is not like a flag to show/hide, more like a bunch of pre-baked percentages to calculate a number, or numbers/functions that are used to calculate positions, styles. 3. state that it has to send to third part, typically your own backend but also any other public services has some expectations on what it needs and that is under this category I don't believe that there is need for more categories here, and so far no one has tried to point out any state that is not under either of those (would be interesting to see such an example though!) Without further rules over how your components are built, potentially any react component can have any or all of these, what makes little sense to have a component that has neither.

Although this is still not the most useful probably, but it helps me to see what I am doing if I can put them in their right place since they usually are read and written at the same place/time

Sometimes I try to omit one (usually the last one) and it's always a mess 😄

I am not experienced with cljs, but I expect these could have their own namespace/spec/tests and the rest of the app should use them, but since I am coming from js where circular dependencies are sometimes ok, it's probably not going to work just as easily

it also don't really make sense to me to use a different namespace's values in a simple react component, but I guess this is more just aesthetics, react is allowing "too simple" constructions that are not complex enough to be useful, but are simple enough to start out looking very promising and very neat

Now, you can put either of these three into either local or global scope, 1. 2. people don't really like in global, but then 3. is never this explicit, if you try to make just that global then you need some kind of system to connect the two

we at work use a custom js react hook called useElmish that mimics the Elm architecture

but any effector system that decouples effects from dispatches is fine, so your state updates are pure, I am probably getting overly detailed, so I stop 🙂

@lilactown found out yesterday what you meant when you said macros can’t return macros. I thought I had some clever way of doing it by having a component functions return syntax quoted data and then having a render macro expand them, but all of the ways I tried couldn’t that to work, always some nested form that wasn’t evaluated. I guess now I know why Helix works the way it does 😅

😄 1

A macro can return a call to a macro though

Which will continue to be expanded further

that is true. what I’ve attempted before (and ran into issues) is macros defining other macros, e.g. (defnc my-component []) would create a new macro my-component in the namespace. doesn’t work in CLJS due to macros needing to exist in a CLJ namespace

But ya, no higher order behavior with macros. You can't return one and say pass it around to something else to use.

Hum, ya that's interesting. ClojureScript does throw additional ranches into it

@didibus but if the macro symbol that was returned is dynamic couldn’t get it to work

considered trying eval but that’s not bundled by default in cljs

Ya that's the ClojureScript limitation (I believe). All macros must be expanded at compile time, and in ClojureScript, compile time can not be interleaved with runtime.

honestly what I’m trying is something i feel should work but i figure doesn’t because of extra Clojurescript caveats

just to get it off my mind i’ll put it here

like lets say this is inside a macro (component props)` where component can be a function or a macro, important bit is that it returns more macro data, i.e. (compiler props children) it seems like since component was dynamically called, even though compiler is a macro it’s not expanded and so in clojurescript runtime i’m just left with that unevaluated s-expression

I don’t know if you can pass in a macro as a value like that

it works for another use-case I tried that was one level deep, but here i think because it’s a macro/function call that outputs more macro data (2 levels deep), couldn’t get it to work

hmm yeah I just tested this: (defmacro foo [] “asdf”) (defmacro bar [s] `(~s)) (bar foo) ;; => “asdf”

that’s in self-hosted CLJS tho - I’m on mobile using Replete

That piece of code, it doesn't make sense

You're escaping all the unquote. You might as well write (component props)

those are dynamic arguments (wasn’t a full example, just for sake of discussion)

Ah ok, also, ya I'm wrong as well. It does delay the function call till later

What do you mean here by dynamically called?

I suspect it might be the unquote qualified expansion that is messing it up

I'm also testing in self hosted replete though :p so can't say for sure

But I think unquote expands to a ClojureScript namespace

But your component macro has a Clojure namespace

What if you fully qualified it to the exact Clojure macro?

no the issue wasn’t that it wasn’t being called (example @lilactown posted for example works)

the issue was that the output wasn’t being called

so say (~component ~props) expands to (my-component {}) and that’s a function that once called outputs (compiler {} children) .. that last part, compiler , is also a macro that I want macroexpanded

2 levels deep, not one

Oh, but with a function in the middle? Ya that won't work

Because the function will evaluate later, after the JS has already been compiled

yea that’s what I’ve learned. but the necessary info is available at compile time so I was hoping it would work

So compiler is treated like a function by ClojureScript as well, and then you'll just see the return value of unquote

Well, why not make my-component a macro?

Or return `(compiler component ~props)

thinking about it, idt this would work since I need to access to that args that are closed in inside the component function

hmm i didn’t try that

kinda reluctant to try lol, working with cljs macros sucks up so much time

going to try it later and let you know if it works, thanks for suggestion @didibus

Ya macros in cljs are extra confusing.

Though I'm really surprised that 2 level deep macros didn't work, feel that should for sure work

is macro expansion expected to be recursive? i.e. macro outputs another macro s-expression, so the output is* also macro-expanded? i know it technically could work (info available at compile time) but figured it’s just not allowed

Outer macros expand first, and then it recursively expands until there are no more macros

So the outer macro expands, and the code it returned is then also macroexpanded, etc.

At least in Clojure

yea that’s how it works if the syntax quoted macro definitions are static, but not sure if that’s meant to work if they are dynamic

In Clojure it would, because macros can expand at run-time as well

Though I feel even on ClojureScript, nested macros in macros should still expand recursively at compile time

this is a simpler alternative that shows a summary of issue with nested macros in code

(defmacro defnc "Create component with given `tag` name and `body`."
     [tag _bindings & body]
     `(defmacro ~tag [props#]
        ;; This nested expression needs to be syntax quoted so we can pre-compile props 
        ;; But since it is two levels deep then it isn't evaluated as cljs code by the Clojure compiler.
        ;; So at runtime, rather than having a fn call that creates a js object, we have a list of Clojure data.
        `(.createElement react Fragment ~props# ~~@body)))
so even if we can call nested macros dynamically, their nested output isn’t actually evaluated

👌 1

Try with clojure.core/defmacro