4 ms·
Reading this, I can't help but feel like the "reject modernity, embrace tradition" UI development trend of returning to untyped templating languages is throwing
by jitl 3y ago
Reading this, I can't help but feel like the "reject modernity, embrace tradition" UI development trend of returning to untyped templating languages is throwing the baby out with the bathwater. Using jinja/htmx/go-templates helps you avoid a big Javascript bundle, but it lands you right back in this 90s/00s toil of screwing around binding your "real" programming language to some half-baked untyped meta-language that spits out something XML-like. If you're picking Rust or any other language where you like the typechecker, why throw that all away?
Defining your UI as more-or-less normal functions in your main programming language that take normal arguments and return return composable UI-typed values is a massive, massive productivity win. Lots of teams pick Typescript/React over other languages/frameworks they prefer or that have better performance for only that reason!
I'm very happy that many production UI systems are moving towards a React/Elm-inspired API. On Android, Jetpack Compose means I don't need to screw around copy pasting method names between XML files and Java classes. On iOS, SwiftUI means I never need to plumb delegates around Interface Builder again. In Typescript land, there's a zillion React-inspired frameworks, like Astro, that carry this learning forward.
If I was in OP's position, I'd be looking for some macros / sugar (https://docs.rs/typed-html/latest/typed_html/ https://docs.rs/typed-html/latest/typed_html/ came up in a quick google search) that make it really easy to write composable UI snippets in my regular Rust files. Why introduce a new language, a new typechecker, and muck around iteroperating between Rust and Typescript, if you can do it all in Rust? You can even add a little bit of glue to make HTMX directives that eg do form things type-check against the form action they'll POST to. You should be able to rely on the Rust compiler to make function calls that return strings not error at runtime.
- deleted 3y ago[deleted]
- 0atman 3y agoThis is the right answer. How can you go back to a non-typed world after Rust I don't know. I use html-node which has a typed submodule for even greater strictness. I just build some valid html, make a string and write that to the output directory and get on with my life. Eg: https://github.com/0atman/noboilerplate/ https://github.com/0atman/noboilerplate/ Templates... Rust functions Flow control... Rust match expression Abstraction... Rust modules Html validation... Rust compiler I like to think that in writing the html-node crate, Vidhan taught the compiler to speak html! https://lib.rs/crates/html-node https://lib.rs/crates/html-node
- usrusr 3y agoWhenever I encounter a template language with expressions reaching into some view model or worse, I come back to an old idea of mine: what if you strictly wrote template first? The templates come up with names, types for the data identified by the names and nesting/optionally/repetition relationships, and the build process generates an interface for the programming language side to implement. Interface as in java interface, and curiously also as in typescript interface, despite the huge difference. A template-first viewmodel, including composability where a type is a reference to some other template. Does this exist somewhere? My impression is that the tedious use of half-baked expression language to pull content from the (typed or not) programming language world into the template has always remained not quite enough of a pain point to drive this reversal and the rise of approaches that don't use separate templates at all (react et al) has made interface-from-template even less likely to happen.
- tubthumper8 3y agoInteresting idea! As an example, if the template was "<span>{name}</span>" then if I follow you correctly, it would generate some `Model` struct/interface/whatever that would have a `name: String` field. I think a problem comes about when it's instead: "<span>{itemCount}</span>" Obviously it can't know that this intends to be an integer. Would it generate an `itemCount` field that is something generic like "impl Display"? Or still String? Either way, you may have other logic that needs this `itemCount` as an integer, for arithmetic or something. Perhaps two models, both auto-generated for all String fields but one you don't touch and the other you can change fields as long as they're convertable to String. I think the main problem is going from a more structured to less structured (model to template) is possible but going the reverse way (template to model) can't always infer the structure.
- jitl 3y agoWell, you’d need to express the types of the arguments your template expects, and then your template type system and your host language type system need to agree on those types, and now you need to code-gen the host language struct type from the template args type - but what’s the point? It’s still needless toil in my view. Just write a function in the host language, it already has a great way of expressing the types of arguments passed to a function (hopefully)