Fork me on GitHub
#reagent
<
2017-09-05
>
Yehonathan Sharvit08:09:30

Can somebody elaborate about the “Perfomance” part of reagen’ts README 1. Reagent should even be faster than plain old javascript React a lot of the time, since ClojureScript allows us to skip a lot of unnecessary rendering (through judicious use of React’s shouldComponentUpdate). 2. The ClojureScript overhead is kept down, thanks to lots of caching.

Yehonathan Sharvit08:09:08

I know that immutable datastructures can be compared very fast (by reference)

Yehonathan Sharvit08:09:21

But I’d like to have a concrete example of it and maybe have a benchmark of reagent vs react.js

curlyfry09:09:49

I don't have anything super concrete unfortunately, but perhaps the official React docs could be an inspiration? They have examples of how shouldComponentUpdate works, and recommend not mutating data (there's also a section on "using immutable data structures"). https://facebook.github.io/react/docs/optimizing-performance.html#avoid-reconciliation

curlyfry09:09:29

There's also a slightly longer section on performance here: http://reagent-project.github.io/

Yehonathan Sharvit11:09:14

But I’m preparing a talk about the advantages of reagent of react.js and I’d like to bring a concrete example

deg19:09:10

I'm working on a project where I have components defined by macros. Basically, these macros do some parameter manipulation, and then emit different components. (e.g. (na/container {:text-align "left"} ...) will expand to [sa/Container {:textAlign "left} ...]) I'm finding that this breaks in some surprising ways. Before I dive too deep, is this even expected to work, or will it get in the way of the Reagent magic that parses components? One example (though I'm still trying to understand the problem, so this may be noise): Inside a component, the first of the following forms work as I would expect. The other two fail in different ways (details possibly not relevant, as they may just reflect bugs in my macrology), even though I think the first and second should be equivalent and the third should not be very different.

(into [na/list-na {:horizontal? true}]
         (map (fn [key] [na/list-item {} key]) keys))
   #_
   `[na/list-na {:horizontal? true}
                ~@(map (fn [key] [na/list-item {} key]) keys)]
   #_
   `(na/list-na {:horizontal? true}
                ~@(map (fn [key] [na/list-item {} key]) keys))

noisesmith20:09:37

the reagent component parsing isn’t magic, it’s a straightforward data to execution thing, in fact what you have there could be done with a function, not needing a macro at all

noisesmith20:09:00

as long as what it outputs is a vector with the right contents, reagent doesn’t really care how you constructed that vector

deg20:09:33

Thanks. That's what I had expected. So, either I'm being blind to some typo (quite possible) or I don't understand what's different between the first two forms above.

noisesmith20:09:13

well, the first one doesn’t splice… n/m I see now you used into, duh

noisesmith20:09:26

I don’t see how those would be different but there must be something going on - you could check if the expanded output of the macro is equal to the into call

deg20:09:58

Yup. had wanted to check first if there was anything known weird about the way reagent parses. https://github.com/reagent-project/reagent/issues/91 was a bit of a red flag. But, I'll start poking deeper.

deg21:09:32

I'm back now... Looks like I was being bitten old assumptions from Common Lisp evaluation. There is a difference, but I need to read up a bit to understand the right fix. ...

trilystro.views> `[na/list-na {:horizontal? true}
                       ~@(map (fn [key] [na/list-item {} key]) [:a])]
[sodium.core/list-na
 {:horizontal? true} 
 [#object[sodium$core$list_item] {} :a]]
trilystro.views> (into [na/list-na {:horizontal? true}]
                       (map (fn [key] [na/list-item {} key]) [:a]))
[#object[sodium$core$list_na]
 {:horizontal? true} 
 [#object[sodium$core$list_item] {} :a]]

deg21:09:59

Ok, fixed; my bad. Just needed to unquote `na/list-na too. I don't understand Clojure eval semantics quite enough to understand why, but I guess this is the wrong channel for that discussion.

noisesmith21:09:05

`[+] returns a vector containing the symbol +, which isn’t the function, but when returned from a macro it should result in a form containing the actual clojure.core/+

deg21:09:26

I didn't understand the second half of your sentence. ...

noisesmith21:09:56

macros substitute their usage with the form they return

deg21:09:55

That's what I thought. But, putting ~ before na/list-na changes this form to behave correctly.

deg21:09:17

Both in the repl, and in practice inside a larger component.

noisesmith21:09:44

that means that the value of n/list-na at the time the macro is expanded is correct, but the value it resolves to in the namespace using the code is not correct

deg21:09:46

Aha. I'll have to wrap my head around that one. Last time I used macros in anger was in Common Lisp, so namespaces introduce new concepts.

deg21:09:02

So, what is the correct way to write this kind of form, where the multiple levels of macros are in different namespaces. ... But, that's probably not a fair question. Let me rephrase to: "what is a good resource I should read to better grok writing Clojure macros?"

juhoteperi21:09:51

Very simplified answer: Don't use macros, usually you don't need them.

deg21:09:47

Agreed, and I've avoided them for a few years of Clojure. I think I need them here, for syntactic reasons, but will revisit in the morning. Meanwhile, just in case, my question stands.

noisesmith21:09:16

I don’t think you need them, because hiccup in reagent isn’t a syntax, it’s just a special way to interpret data structures

noisesmith21:09:36

unless you intend to expand hiccup to something that data structures can’t do

juhoteperi21:09:50

Google search for "clojure macros" provides many good results, I can't now remember which ones I have found most useful.

deg21:09:53

Yes, but what I'm letting the user write something that looks like Hiccup, and is used syntactically in the same way as any other piece of hiccup (without any wrapper function), but needs to be treated as different hiccup.

deg21:09:23

All that being said, I agree that I may have a blind spot here, and will take a closer look to see if I can do this as a function. But, I don't think it is possible.

juhoteperi21:09:56

@deg Do you have some examples? I haven't read the complete backlog, but e.g. (na/container {:text-align "left"} ...) -> [sa/Container {:textAlign "left} ...], here na/container can be a function which returns vector

deg21:09:20

Yeah. Too late to think straight here (after midnight) but I will run through all my cases in the morning to see if this is always true.

deg21:09:43

good night and thanks.

reefersleep21:09:37

Just want to chime in; at my work, I've been surprised many times with what can be achieved through just functions and data structures. I think you should definitely try to see if you can achieve what you want without macros as well, @deg 🙂 You'll be much happier if you can achieve this, I think.