Elm i Rust med Webassembly

Booster 2019

Sindre Ilebekk Johansen, Iterate AS
(@sindreij)

Elm - A delightful language for reliable webapps

Rust - Empowering everyone to build reliable and efficient software.

type alias Model =
    { count : Int }


init : Model
init =
    { count = 0 }


type Msg
    = Increment
    | Decrement


update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            { model | count = model.count + 1 }

        Decrement ->
            { model | count = model.count - 1 }


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Increment ] [ text "+1" ]
        , div [] [ text <| String.fromInt model.count ]
        , button [ onClick Decrement ] [ text "-1" ]
        ]
type alias Model =
    { count : Int }


init : Model
init =
    { count = 0 }


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Increment ] [ text "+1" ]
        , div [] [ text <| String.fromInt model.count ]
        , button [ onClick Decrement ] [ text "-1" ]
        ]

type Msg
    = Increment
    | Decrement


update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            { model | count = model.count + 1 }

        Decrement ->
            { model | count = model.count - 1 }

MODEL

view

VDOM

render

DOM

Update

MODEL'

VDOM'

msg

view

#[derive(Clone)]
struct Model {
    counter: i32,
}

fn init() -> Model {
    Model { counter: 0 }
}

enum Msg {
    Increment,
    Decrement,
}

fn update(msg: &Msg, model: &Model) -> Model {
    let mut model = model.clone();

    match msg {
        Msg::Increment => model.counter += 1,
        Msg::Decrement => model.counter -= 1,
    }

    model
}

fn view(model: &Model) -> Html<Msg> {
    div(
        &[],
        &[
            button(&[on_click(Msg::Increment)], &[text("+")]),
            div(&[], &[text(&model.counter.to_string())]),
            button(&[on_click(Msg::Decrement)], &[text("-")]),
        ],
    )
}
type alias Model =
    { count : Int }


init : Model
init =
    { count = 0 }


type Msg
    = Increment
    | Decrement


update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            { model | count = model.count + 1 }

        Decrement ->
            { model | count = model.count - 1 }


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Increment ] [ text "+1" ]
        , div [] [ text <| String.fromInt model.count ]
        , button [ onClick Decrement ] [ text "-1" ]
        ]
fn div(children: &[Html]) -> Html {
    [???]
}

fn button(children: &[Html]) -> Html {
    [???]
}

fn text(s: &str) -> Html {
    [???]
}
pub struct Model {
    counter: i32,
}

fn init() -> Model {
    Model { counter: 0 }
}

fn view(model: &Model) -> Html {
    div(
        &[
            button(&[text("+")]),
            div(&[text(&model.counter.to_string())]),
            button(&[text("-")]),
        ],
    )
}
enum Html {
    Tag {
        name: String,
        children: Vec<Html>,
    },
    Text(String),
}
<div>
    <a>Hello World</a>
    <p>This is a test</p>
</div>

name

children

fn div(children: &[Html]) -> Html {
    Html::Tag {
        name: "div".to_string(),
        children: children.to_vec(),
    }
}

fn button(children: &[Html]) -> Html {
    Html::Tag {
        name: "button".to_string(),
        children: children.to_vec(),
    }
}

fn text(s: &str) -> Html {
    Html::Text(s.to_string())
}
#[wasm_bindgen]
pub fn start() {
    let model = init();
    let vdom = view(&model);
    render(&vdom)
        .expect("Error rendering");
}

magi (wasm-pack + wasm-bindgen)

// index.js
import * as wasm from '../pkg';

wasm.start();
fn create_node(vdom: &Html) -> Result<Node, JsValue> {
    let window = web_sys::window()?;
    let document = window.document()?

    match vdom {
        Html::Tag {
            name,
            attrs,
            children,
        } => {

            let res: HtmlElement = document
                .create_element(&name)?
                .dyn_into()?;
            let res: Node = res.into();

            for child in children {
                let node = create_node(&child)?;
                res.append_child(&node)?;
            }

            Ok(res)
        }
        Html::Text(text) => {
            let res = document.create_text_node(&text);
            Ok(res.into())
        }
    }
}
function create_node(vdom) {

    let document = window.document;

    if (vdom.type === 'tag') {






        let res = document.createElement(vdom.name);
        



        vdom.children.forEach(child => {
            let node = create_node(child);
            res.appendChild(node);
        })

        return res;
    }
    else if (vdom.type === 'text') {
        let res = document.createTextNode(vdom.text);
        return res;
    }
}
fn render(vdom: &Html) -> Result<Node, JsValue> {
    let window = web_sys::window()?;
    let document = window.document()?;

    let parent: Node = document
        .get_element_by_id("app")?
        .into();

    let root = create_node(vdom)?;

    parent.append_child(&root)?;
}
function render(vdom) {

    let document = window.document;

    let parent = document.getElementById("app");



    let root = create_node(vdom);

    parent.appendChild(&root)?;
}

MODEL

view

VDOM

render

DOM

Update

MODEL'

view

VDOM'

???

msg

https://github.com/sindreij/willow

@sindreij

Made with Slides.com