
Typed-Html: Type Checked JSX for Rust - fanf2
https://github.com/bodil/typed-html
======
gkoberger
One thing that's really cool about Rust is you don't need a compiler (like
JSX) for this. It's using a procedural macro, so the Rust parser only sees the
result of the macro.

Basically, with Rust, you can import syntax like this much like you'd import a
library in other languages.

~~~
yen223
It's such a shame people think languages shouldn't have macros because they're
complex. They _reduce_ complexity by shifting custom bespoke syntax out of the
language core, and into packages that can be used (or _not_ used) as needed.

~~~
pjmlp
Because most of the time they represent a huge debugging effort.

For example, how do you step a macro expansion in Rust at any given point in a
source level debugger?

Yep, you don't.

Even on Lisp, if we want something more developer friendly than
_(macroexpand)_ the only option are the commercial Common Lisp environments.

Having said this, I also do like having macro support around, provided it gets
used judiciously.

~~~
bluejekyll
This isn’t what you want, but I do want to make sure that people know that
there are tools available for at least viewing the expansion:
[https://github.com/dtolnay/cargo-
expand/blob/master/README.m...](https://github.com/dtolnay/cargo-
expand/blob/master/README.md)

~~~
pjmlp
Thanks, it looks interesting.

Any plans to make it work on stable?

~~~
steveklabnik
It doens't have a ton of pressure to, given that it's a tool. You write "cargo
+nightly" instead of "cargo" and it's fine.

We'd of course like to have it on stable, like everything, there's just more
pressing stuff to stabilize.

------
bluejekyll
This is super exciting and awesome. I worked with the html! macro from yew,
[https://github.com/DenisKolodin/yew](https://github.com/DenisKolodin/yew),
and it had some quirks. Assuming yew hasn’t improved that macro since I used
it (5 months back), this looks significantly better in almost every way. It
would be very cool to see this become the main library for html generation in
Rust (and WASM?). I’ve been following the posts about this library’s
development, fun to see it discussed here.

But I have to ask, will the choice of the MPL as the license have any effect
on its potential adoption? Yew is Apache/MIT, matching Rust and much of the
ecosystem, for context.

~~~
drostie
It's possible but unlikely.

MPL, for folks who aren't aware, is a successor to the CDDL seen in Solaris
etc. and is a per-file copyleft, thus having a nicer legal structure while
getting the same rough benefits of GPL or at least LGPL. The idea is “any
modifications to this file must also be open-sourced under MPL, but you can
package this file with proprietary other files that are not MPL and integrate
them into a larger proprietary thing as long as your modifications to this
file alone are open-sourced.” The goal is to protect weakly from Microsoft-
esque “embrace, extend, extinguish” as GPL does but enable commercial
integration the way BSD does.

In practice nobody seems to be all that pissed at Mozilla because of their
license; the MPLv2 added GPL compatibility so the GPLers are mostly able to
use MPL software and it gives a nod towards the stronger copyleft they like;
commercial applications which just use the software as a library don't mind.

~~~
jdub
Minor historical nitpick: The CDDL was based on the MPL. There used to be a
lot of licenses that were pretty much the MPL with the names changed.

~~~
drostie
Thanks, I appreciate the correction!

------
hbbio
It's funny that the modern web stack (JSX syntax, type checking, async/await,
etc.) is now heading towards something we built 10 years ago:
[http://opalang.org](http://opalang.org)

I'm very glad that this is happening, the whole project got started when I was
struggling to write PHP/JS in the early 2000s and the itch was getting worse
and worse.

~~~
tannhaeuser
I find it funny that JavaScript had (proposed) standardized first-party
support for XML literals in the language (E4X), but nobody was using it
because "XML sucks". Then shortly after it was scraped by Mozilla, React/JSX
(requiring babel/node) became mainstream and praised for basically what E4X
had done all the time. I guess to become mainstream these days, a lib needs to
spin a drama around it such as "React vs Angular".

~~~
okal
This seems to happen quite a lot. I've heard similar criticism levelled at
GraphQL (as a modern day SOAP). I wonder what the psychology behind this is.
Perhaps they were ahead of their time, and top-down, not "community-driven"?

~~~
hopsoft
Interesting point about grassroots vs top down. I think that definitely plays
into the equation. Another big part of it is that developers seem to a unique
class of workers that don’t seem learn much from those who preceded them. We
love to reinvent things, in part due to the snowflake effect & NIH syndrome.
The other reason I think the wheel keeps being reinvented in tech is due to
the developer workforce essentially doubling every 5 years... also, the sad
reality that ageism coupled with very few long term viable career tracks
available for software writers pushes a lot of experience and wisdom out of
the industry.

------
tannhaeuser
How is this checking the content type of HTML elements? The content type of
eg. the table element in HTML 5.1 is given by the following production (from
[1]):

    
    
        <!-- The table element
         Content: In this order:
         optionally a caption
         element, followed by zero
         or more colgroup
         elements, followed
         optionally by a thead
         element, followed by
         either zero or more tbody
         elements or one or more
         tr elements, followed
         optionally by a tfoot
         element, optionally
         intermixed with one or
         more script-supporting
         elements. -->
        <!ELEMENT table - - 
         (caption?,colgroup*,
          thead,
          (tbody*|tr+),tfoot?)
         +(%scripting;)>
    

Regular content types are pretty fundamental to markup languages.

[1]
[http://sgmljs.net/docs/w3c-html51-dtd.html](http://sgmljs.net/docs/w3c-html51-dtd.html)

~~~
zootm
I think this is mostly answered in the README. This is probably the most
relevant section:

> The structure validation is simplistic by necessity, as it defers to the
> type system: a few elements will have one or more required children, and any
> element which accepts children will have a restriction on the type of the
> children, usually a broad group as defined by the HTML spec. Many elements
> have restrictions on children of children, or require a particular ordering
> of optional elements, which isn't currently validated.

It's not complete or thorough at present.

Regarding your link: I'm impressed someone took the time to write a DTD for
HTML5!

~~~
oever
Since the macro is procedural, I suppose it's feasible to pass a DTD, XML
Schema or Relax NG to typed-html so it would work with any XML. Ideally with
checking of ID and IDREF or key/keyref.

~~~
tannhaeuser
The question is then can Rust's macros (or what typed-html's technique is
called) encode static/compile-time type checking for regular content models,
with SGML-like content exceptions and, to top it, with SGML/HTMLish omitted
tag inference?

------
gregwebs
Nice! It is quite wonderful to know that if your code compiles there are no
run-time issues in your templates. Of course, you can achieve this with
TypeScript also, but it is easy to introduce errors while making API calls.

Haskell has had something similar for many years now [1]. Ultimately the
development cycle can become a big issue for this kind of thing (how long does
it take to go from editing your type-checked template to being able to see
that in the browser). In the Haskell version we did have a development version
that allowed certain types of changes to show up quickly.

[1] [https://www.yesodweb.com/book/shakespearean-
templates#shakes...](https://www.yesodweb.com/book/shakespearean-
templates#shakespearean-templates_hamlet_html)

------
rwmj
ocsigen does this for OCaml:

[https://ocsigen.org/eliom/1.3.4/manual/html](https://ocsigen.org/eliom/1.3.4/manual/html)

The syntax is fairly lightweight, just << followed by valid, type checked
XHTML ended by >>. There are also antiquotations so you can use it as a
templating language.

For those of us generating XML from C I wrote a hairy set of C macros:

[https://github.com/libguestfs/libguestfs/blob/master/common/...](https://github.com/libguestfs/libguestfs/blob/master/common/utils/libxml2-writer-
macros.h)

so you can write code like this (which is not fully checked at compile time of
course):

[https://github.com/libguestfs/libguestfs/blob/4aa712d551f9d4...](https://github.com/libguestfs/libguestfs/blob/4aa712d551f9d4f5cda2b01d3fe5c672f531142b/lib/launch-
libvirt.c#L1068)

~~~
Drup
Regarding ocsigen, It's more appropriate to point to tyxml[1] and its syntax
extension [2]!

Note that tyxml goes quite further than Rust's typed-html: the nesting is
significantly more flexible, type inference is still complete, and it will
verify additional properties like "don't use <a> inside <a>". It can also be
used conjointly with reactive and/or isomorphic programming.

[1]: [https://ocsigen.org/tyxml/](https://ocsigen.org/tyxml/) [2]:
[https://ocsigen.org/tyxml/4.3.0/manual/ppx](https://ocsigen.org/tyxml/4.3.0/manual/ppx)

~~~
jonathanyc
As an OCaml user, I do not see at first glance how any of your statements
about where tyxml “goes quite further” are true, with the exception of
completeness on the Rust end (but I am very dubious that there is completeness
on the OCaml end—HTML is not as simple as you’d think). This makes me think
that if you’d like to convince people who are not OCaml users to look at your
library, you should provide examples of why these things are true and how they
are useful. Otherwise, we just come off as smug FP weenies.

------
Drup
As the maintainer of a similar OCaml library[1], questions to the authors:

\- How compositional it is ? I have find that some of the HTML properties are
very hard to verify in a compositional way, see
[https://github.com/ocsigen/tyxml/issues/175](https://github.com/ocsigen/tyxml/issues/175)

\- How do you handle the "subtyping" aspect that is intrinsic to HTML ? Or
phrased in another way: what's your type encoding ? :)

\- I suppose you desugar to a set of combinators, but you don't really expose
those. Why ?

[1]: [https://github.com/ocsigen/tyxml/](https://github.com/ocsigen/tyxml/)

~~~
twic
> I suppose you desugar to a set of combinators, but you don't really expose
> those. Why ?

Why indeed. I personally find these "HTML in a programming language" tools
pretty unappealing, because you have to give up all the normal tools of the
programming language while using them [1].

Whereas you can usually write a really simple internal DSL to describe HTML
that is as clear and concise as HTML, while being as convenient and powerful
as the programming language. My quick and dirty efforts at this usually end up
looking something like this:

    
    
        html(
            head(
                title("My First Page")),
            body(
                h1("Welcome"),
                p($("Made by "), a(href(), $("Tom"))),
                userIsOldSchool()
                    ? p("Bring back HTML 3.2!")
                    : div("Made with HTML 5.")))
    

The typed-html crate has an object model like this underneath, but doesn't,
AFAICS, expose it.

[1] With the honourable exception of ScalaTags, which i still wouldn't touch
with a bargepole

~~~
Drup
Yes, that's why in Tyxml, we have a set of combinators _and_ an HTML-like
syntax, and you can compose them arbitrarily with each other. This way, you
can use the HTML syntax (and c/c internet snipets) but still enjoy all the
cool functional idioms.

------
TheAceOfHearts
This is superb, I've been playing around with Rust and had been wanting to dig
into doing some web stuff with it. What tools do people use for writing web
services with Rust?

A component example would be very useful. Being able to abstract away chunks
of your views into discrete parts is a pretty important feature.

One big thing that's not mentioned in the docs is whether or not it handles
escaping values for you, as well as an equivalent of React's
dangerouslySetInnerHTML [0] when you wish to embed HTML.

[0] [https://reactjs.org/docs/dom-
elements.html#dangerouslysetinn...](https://reactjs.org/docs/dom-
elements.html#dangerouslysetinnerhtml)

~~~
mark_l_watson
I am in the same situation: I have lightly played with Rust (because a DLT
platform I am interested in is written in Rust) and I want to branch out a bit
and try Rust for a fairly simple web app.

------
calebh
Does anyone have a good way of representing HTML in the form of types? I'm
primarily thinking about how it's tricky to pair valid attributes with the
correct elements. For example, it should be okay to use the "alt" attribute
with the img tag, but not with the p tag. It seems like there should be a
type-safe way to do this.

~~~
dtolnay
This library represents HTML in types. The <p> tag turns into a
typed_html::elements::p [1], inside of which the attributes are held by a
typed_html::elements::Attrs_p [2].

[1] [https://docs.rs/typed-
html/0.1.0/typed_html/elements/struct....](https://docs.rs/typed-
html/0.1.0/typed_html/elements/struct.p.html) [2] [https://docs.rs/typed-
html/0.1.0/typed_html/elements/struct....](https://docs.rs/typed-
html/0.1.0/typed_html/elements/struct.Attrs_p.html)

    
    
        let doc: DOMTree<String> = html!{
            <body>
              <img alt="xxx" />
              <p alt="xxx"></p>
            </body>
        };
    

If you try to give <p> an alt attribute, the program fails to compile.

    
    
        error[E0609]: no field `alt` on type `typed_html::elements::Attrs_p`
          --> src/main.rs:12:16
           |
        12 |             <p alt="xxx"></p>
           |                ^^^ unknown field
           |
           = note: available fields are: `accesskey`, `autocapitalize`, `class`, `contenteditable`, `contextmenu` ... and 9 others

~~~
calebh
This solution bundles all of the attributes into the same type, which comes
with the downside that it's impossible to represent a single attribute without
also receiving all the other attributes.

I think that the best solution in Haskell would be to group the types in a
typeclass. However then the typeclass is not directly associated with the p or
img value constructors. I wonder if there is some way to use dependent types
to improve this situation.

------
rattray
Would be awesome to see an example of this with a full vdom implementation...
the docs just say:

    
    
        Render to a virtual DOM...  pass it on to your favourite virtual DOM system
    

Curious what'd it be like to wire this up with virtual-dom or maybe even React
itself.

Also curious how this compares to
[https://github.com/DenisKolodin/yew](https://github.com/DenisKolodin/yew)
(which I'm not familiar with).

~~~
bluejekyll
I used yew’s html! macro for a small experiment about 5 months ago. It’s
awesome. The difference between that and this, is mainly this is more
typesafe. This will check the types and expected attributes of tags, whereas
yew pretty much treats all tags the same way.

------
sidcool
Rust (and the community) is getting these amazing tools out. As a Rustacean
it's quite comforting . :)

------
andraaspar
No components??? It's no good then.

Edit: Sure, having automatic HTML escape for values is good. I work with JSX
every day, and I can't really imagine having it all in one huge chunk and no
code reuse. Also, what about conditional hide/show and lists?

~~~
RussianCow
The very first example in the README shows how to render a list using `map`,
duplicated here[0]. I don't know about conditional rendering; the README
doesn't have any examples of that, and there doesn't appear to be any actual
documentation.

Edit: You could probably create a separate function with an `html!` return and
call that from the first? If the return types line up, which they should, I
don't see why you couldn't do that.

[0]: [https://github.com/bodil/typed-
html#example-1](https://github.com/bodil/typed-html#example-1)

------
pjungwir
I'm sad that SPAs have made us regress from haml back to plain html. Without
closing tags, a whole class of bugs just goes away, and you can read your
document structure at a glace. (Ease of reading is much more valuable than the
saved typing in my opinion.) In my own React work I've found it nicer to use
pug for templating, instead of jsx, and setting it up in webpack was just a
couple lines. On team projects it's hard to get consensus for something like
that though. I'd love to see a WASMable whitespace-sensitive html templating
library for Rust. I bet it would even give the tokenizer a break so you don't
have to quote the strings.

~~~
err4nt
HTML syntax is a lot more flexible than a lot of people choose to write it.
The following is a valid table in HTML:

    
    
        <table>
          <caption>Example Table
          <thead>
            <tr>
              <th>Col 1
              <th>Col 2
              <th>Col 3
          <tbody>
            <tr>
              <td>One
              <td>Two
              <td>Three
          <tfoot>
            <tr>
              <td>Four
              <td>Five
              <td>Six
        </table>
    

I'm currently drafting an article of HTML you never need to write, currently
I'm covering:

\- some start tags are implied and never need to be written unless you're
adding attributes

\- some end tags are implied and never need to be written

\- default attributes never need to be specified

\- element values often don't require quoting

\- no trailing slashes are required on void tags

------
leetbulb
This is really cool. How viable would it be (resource consumption, etc) to use
it for embedded firmware? Are there any special security considerations such
as XSS?

------
Dowwie
Any word on when proc macro hygiene functionality will release to stable? This
project and Maud both depend on nightly features such as this.

~~~
dtolnay
This library no longer requires proc_macro_hygiene. I added support for the
stable compiler in [https://github.com/bodil/typed-
html/pull/1](https://github.com/bodil/typed-html/pull/1).

~~~
steveklabnik
Ahhh thank you a ton, this is awesome.

------
equalunique
Bodil Stokke has many other interesting projects. Check out her other repos
too.

------
abhinai
I really think all modern languages should have something like this!

------
bhengaij
Obligatory mention- scalatags. Something different but still wicked cool

~~~
hayd
> any editor that understands Scala will understand scalatags.Text. Not only
> do you get syntax highlighting, you also get code completion:

Wow.

~~~
bhengaij
The entire Scala front end env is very underappreciated. And I say this as a
fan of TypeScript creator.

------
akubera
f

------
biggio
I always hated mixing up languages together.

HTML in Rust? No!

~~~
brailsafe
I guess in this case it'd actually be a sort of pseudo javascript in html in
rust kind of thing

~~~
hobofan
> pseudo javascript

There is no pseudo javascript involved here.

~~~
brailsafe
pseudo javascript-in-html

~~~
hobofan
No. Rust-in-html-in-Rust.

