Web development is not magic: cycle.js for rust


#1

I’ve been going through several web development frameworks and learning javascript and typescript. After learning Angular2 and being convinced to check out React, I got put on a wild-tailspin of checking out Redux then RxJS and then, finally, Cycle.js.

It is at Cycle.js that I stopped and finally fully realized that developing on the web is not magic and does not require a huge number of modules (javascript just hit their 350,000th node.js module!). Their dialogue abstraction tutorial section is worth checking out in-and-of itself to understand how creating user interfaces (not just on the web) can be made to be exremely simple through functional programing concepts. Not only that, but their whole library, with all necessary ecosystems to get most web pages up and running, comes in at something like a few thousand lines of code.

However, now that I am using rust I am completely poisoned with my need for a good compiler. I’m setting up a source stream with this tidbit:

   const changeWeight$ = sources.DOM.select('.weight')
     .events('input')
     .map(ev => parseInt((<HTMLInputElement>ev.target).value));

basically this selects the ‘input’ events of the ‘.weight’ html element and converts the events into integer values.

There are several problems here:

  • the event type is not known at compile time and I have to manually cast it as HTMLInputElement– if I cast it incorrectly it fails extremely silently
  • the event.value is a string, and to convert it I use parseInt. However, parseInt returns a NaN if it can’t parse (for an int!). NaN is one of the most silent and difficult to check for values that exist (you must use isNaN function).

It occurs to me that Cycle.js is so simple, so rustic in the way it is almost zero abstraction, that implementing it in emscripten+rust would be the perfect target for the first ever rust web frontend framework. Cycle.rs.

So I guess the question is, is rust ready yet and is anyone interested in doing this? I’m still a javascript newbie and have like a million projects at the moment, but god do I wish this already existed.


#2

One of the things that made me understand that web design is not magic is that you can fully express templates in code using something like snabbdom. There is no need to learn html (but you should learn the Element objects in html) or to tie html to your code in pretty much any way.


#3

I’m very interested in this as well!

You are using TypeScript, right? I’ve heard great things about it, but haven’t used it myself (but I write far too much JavaScript). Let’s see if you can tweak the method signatures a bit to make it nicer.

What I see is that .events('input') is stringly-typed, but luckily, looking at the TS docs, I found this section on “String Literal Types”! (In Rust I’d have suggested defining an enum for that.) It apparently allows you to overload methods like events(type: string): DomEvent based on the value of type. So you can try to add an overload like:

events(type: 'input'): ThingyWhereEventTargetIsHTMLInputElement

The generated DOM types (here, quite large) use this to restrict the input to specific strings.

So… don’t use parseInt directly! Rust has the awesome FromStr trait and see no reason why you can’t write that in TypeScript. Or, just:

type Integer = number;
class FromStrError extends Error { }

function from_str(input: string): Integer {
    const maybe_int = parseInt(input);
    if (isNaN(maybe_int)) {
        throw new FromStrError("Can't convert ${input} to integer");
    }
    return maybe_int;
}

What about the reactive programming model which is one huge abstraction over things that change over time? :wink:

I don’t see any reason why you can’t write cycle.rs, except for, well, a whole tonne of missing libraries you’d have to write first:

  • An rx.js/xstream equivalent (maybe look at the carboxyl and rx crates)
  • Idiomatic DOM bindings (if you want to target the DOM)
    • probably also using asm.js APIs, right
    • with event handling (that works with your rx equivalent)
  • All those cycle.js drivers for stuff like
    • virtual-dom/snabbdom (I wrote some notes on that here)
    • HTTP requests (based on tokio/futures probably)
    • Storage layers (for persistent and UI states)
    • etc.

Sadly, it’s a long way to get something that you can use in the browser… let me know when you get started :slight_smile:


#4

OK, so there might be a few more things that need to be done :slight_smile:

On the other hand, many of them would need to be done for any rust Javascript framework and are fairly straightforward (I would think…), so at least it would be laying the groundwork!

Awesome suggestions, I think I’m opening a feature request to get those typings added :slight_smile:


#5

I would just like to give a shoutout to Elm – the issues I was having finally drove me over the edge and I am finding Elm to be VERY rust-like: it has pattern matching, required enums and extensive compile time checks. Because it is functional (which means immutable-only data) it also has many of the other guarantees of rust (albeit not with the performance gains).

I am also finding it quite simple, and find that the relatively foreign syntax is almost immediately NOT foreign because of me spending the considerable effort in learning rust. Expressions resulting in a value is one thing that would have absolutely killed me before rust – but now it feels completely natural and lets me focus on learning the language.

This highlights two important things: rust really is a well designed language and learning it will help you learn other well designed languages!