-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"RSX" Component builder with macros #56
Comments
No, I'm not very fond of this idea, I've thought about this already. The more general problem I see with JSX is that JSX is essentially just functions, wrapped in an XML-ish syntax. For example, take this JSX: render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is {isLoggedIn ? 'currently' : 'not'} logged in.
</div>
);
} and compare it to this Rust / azul code: fn render(&self) {
Dom::new(NodeType::Div).with_child(
Dom::new(NodeType::Label(
format!("The user is {} logged in.", if self.logged_in { "currently" } else { "not" }))
)
)
} See any similarities? All that JSX is doing is wrapping Other crates like conrod or yew - they can't expose a reasonable API without using macros, because they need boilerplate code - this essentially leaks implementation details into the public API, just that the macros cover it up a bit. I don't see macros as a "feature", I rather see them as a bug - the API has too much boilerplate to be written using functions, so macros are used to cover up things like Rust-String-to-JS-String conversions in yew, for example. One of the main appeals of azul is having a macro-free API and being able to define user interfaces purely by composing functions. Just to show how "declarative" a pure-functional UI can be, here is a function that I wrote for my application: /// Render a key-value pair
fn render_config_field(key: &str, id: &str, value: String) -> Dom<AppData> {
Dom::new(NodeType::Div).with_class("configuration_field").with_id(id)
.with_child(Dom::new(NodeType::Label(key.into())).with_class("configuration_field_label"))
.with_child(Dom::new(NodeType::Label(value)).with_class("configuration_field_value"))
}
/// Render the controls for editing the map
fn render_map_fields(map: &Map) -> Dom<AppData> {
Dom::new(NodeType::Div).with_id("map_configuration_container")
.with_child(render_config_section_header("Map Configuration"))
.with_child(render_config_field("Width:", "map_width", format!("{}", map.width_mm)))
.with_child(render_config_field("Height:", "map_height", format!("{}", map.height_mm)))
.with_child(render_config_field("Center East:", "map_center_east", format!("{}", map.center_east)))
.with_child(render_config_field("Center North:", "map_center_north", format!("{}", map.center_north)))
.with_child(render_config_field("Scale:", "map_scale", format!("{}", map.scale)))
.with_child(render_config_field("CRS:", "map_crs", format!("{}", map.target_crs)))
} See how I didn't have to copy-paste the DOM creation of the key-value-pair everywhere? The question is, how is IMO, there is really no need for such a macro, because you can already efficiently compose the UI via functions (which is something that JS can't, since JS doesn't redraw the screen all the time and has to deal with the stateful browser, so they need React and JSX and all that stuff to deal with the statefulness). Another reason against macros is that macros are always harder to debug than functions. Always. Every time I've used a macro in the past, I've been way better off with functions. Macros are fundamentally a hack around the type system, they should be used only when absolutely necessary and when functions can't be used. Of course, you can make your own crate with extension macros that build on top of azul, you can use the public API for that. Kind of like the Another possibility is to make "shorthands" for typing-intensive things like There is going to be a XML-based document loader (see #29), but that is only intended for prototyping and because of hot-reloading UI files while the app is running. So to finish this very long comment, my suggestion is that if you want this, make it as a separate crate and then I can see if parts of it may be used in the API. I currently have a lot of stuff to do, lots of other bugs to fix and so it would be better if it could be maintained separately, to split the work. |
Relevant: https://github.com/bodil/typed-html |
Yes, typed HTML looks great but it has an entirely different purpose ;) |
Performance has nothing to do with it, you can use react just fine without the JSX. Sorry but I don't see any real arguments except for implementation and debugging complexity. |
I meant the code size (since JS files have to be delivered over the network), JSX is more "compact" than regular JS code, so that's an argument for better performance. I'm not dismissing DSLs, but I am dismissing specializing on a specific DSL in the core library or building the core model of azul around a DSL. You should be able to build a DSL on top of the existing Rust API, not the other way around. DSLs should be syntax sugar, not the core foundation. So, since it's only syntax sugar, there is no reason for a DSL to be inside of azul. Eventually, I will re-export things like the The thing is, DSL flavors come and go, today JSX is the best thing, other people like QML a lot, tomorrow there's something else. And well, "implementation and debugging complexity" is sadly a real argument - I don't have unlimited time. For my own projects that I use Azul for, I am able build UIs without a DSL, so I don't see not having a DSL as such a big problem as people make it out to be. If you are interested in making something QML or JSX - be the change you want to see, that's really all I'm saying. Make a |
@fschutt I think you're a bit mistaken here: JSX compiles to pure JS function calls at build time. See here: https://reactjs.org/docs/introducing-jsx.html#jsx-represents-objects But I agree 100% with you that it should be either a separate crate or something feature-gated! It has no purpose in the core of a UI library (much like JSX is opt-in, you need a babel/webpack transformer to make it into JS React calls). |
@diegor8 I don't use yew or draco, this is the azul repository. I don't know if yew needs macros, ask at the yew maintainers, not me. |
Closing this because this is more or less what the XML system is. Take a look at the <component name="Invoice" args="dueDate: String">
<p>Your invoice is due at {dueDate}</p>
</component>
<app>
<Invoice dueDate="02.03.2019" />
</app> In the future, you will be able to compile that XML code to Rust functions (to have both a performant and type-safe prototyping API), but for now you can use the XML loader as a way to construct RSX-like macros. |
@fschutt So really this feature seems to boil down to is it better to have:
It seems questionable you think that people would rather look at a mountain of nested function calls vs an XML like language as their primary source. Claiming that functional composition makes this all easier reads suspiciously like someone who has never used this tech like this to make a complex UI page. There's a reason why React people don't write h(...) all over the place even though it's an option to them. |
Well, I do, you're just supposed to compile the XML to Rust code at some point. Azul isn't a web browser, the point isn't to make the XML something like HTML, it's only there to enable hot-reloading, (so compile-time macros are out, because they don't run at runtime).
Yes, that's what I meant by "XML-to-Rust compiler". There is already a stub API for that, even though it doesn't work yet. You can either compile your UI files once manually or put this function in your build script.
Yeah because React is functional composition of UI elements, mapping (or "rendering") a model onto the screen, ( Again:
|
I think I understand, I feel a bit silly now, but I do see your point about hot reloading at runtime being the only real way to do that fast in Rust. Thanks for clarifying. I do hope the XML side of this project grows! As someone who has worked with HTML and XAML before, I hope webrender is the future of GUI in Rust :) |
Description
One thing that I feel as though Azul could benefit from is a way to create structured DOMs in a more declarative fashion, if we look to WASM interface frameworks, we can see this idea with how yew allows users to craft interfaces.
Describe your solution
A macro like
dom!
orhtml!
that would allow people to make GUIs in a way that resembles JSX or TSX in frontend web development termsAre there alternatives or drawbacks?
Is this a breaking change
No, the old way of creating UIs could exist side by side with this
The text was updated successfully, but these errors were encountered: