Skip to content

Instantly share code, notes, and snippets.

@spion
Last active August 29, 2015 14:06
Show Gist options
  • Save spion/32e24bd98791e06283dc to your computer and use it in GitHub Desktop.
Save spion/32e24bd98791e06283dc to your computer and use it in GitHub Desktop.

The first thing that caught my eye was

mercury.app(document.body, clickCount, render)

I immediately wanted to know the types of all 3 arguments. Obviously, render is a function that produces a virtual dom description, but it wasn't immediately clear what is clickCount - apparently its an observable application state whose value will be passed to the render function when a change is observed.

A more detailed discussion of the events part would be useful - it seems to be the least straightforward part. Its not clear what transformations are applied by mercury.xxxEvent and mercury.event to the function nor what the return type is. Its was also not clear to me if its possible to use valueEvent to observe the value of another component - e.g. to observe the value of a text field when a "Search" button is pressed - or would I need to combine click with change valueEvents (and how to do that - FRP style perhaps?).

Maybe this part could do well even without value-event. Seems like a distraction/indirection that is not strictly necessary, especially not in the introduction. For example, I had to replace mercury.changeEvent(sink) with function(e) { sink({text: e.target.value}); } to gain some understanding of the shared-state example. (Originally I expected value-event to deliver a value rather than wrapping it within an object)

@Raynos
Copy link

Raynos commented Sep 16, 2014

mercury : {
  app: (
    DOMElement,
    state: Observ<T>,
    render: (T) => VTree
  ),
  struct: (Object<String, Observ<T>>) => Observ<Object<String, T>>,
  value: (T) => Observ<T>
}

We take an observable and a rendering function, we then create a rendering loop with the initial state by unpacking the observable, we call render, we create a dom element, append it as a child to document.body then when the observable state changes we tell the rendering loop to schedule a re-render on the next frame.

@Raynos
Copy link

Raynos commented Sep 16, 2014

Its not clear what transformations are applied by mercury.xxxEvent and mercury.event

The most detailed documentation can be found here https://github.com/Raynos/value-event.

Because of the current state of mercury it's expected you read the source code ( https://github.com/Raynos/mercury/blob/master/index.js#L16-L20 ) then look up the documentation of the relevant "child" module.

@Raynos
Copy link

Raynos commented Sep 16, 2014

Its was also not clear to me if its possible to use valueEvent to observe the value of another component - e.g. to observe the value of a text field when a "Search" button is pressed - or would I need to combine click with change valueEvents (and how to do that - FRP style perhaps?).

Technically it is possible to observe an observable using valueEvent because Observ and Event (from ghub.io/observ and ghub.io/geval) happen to have the same structural type. This is undocumented behaviour.

In the future I am going to move towards handles and you will not be able to do this any more.

@Raynos
Copy link

Raynos commented Sep 16, 2014

Maybe this part could do well even without value-event. Seems like a distraction/indirection that is not strictly necessary,

It is correct that using changeEvent is a bit heavy, however this will be explained better in the future with handles. For now it would be simpler to use inline functions.

I've avoided this in all examples to drive home one of the main strenghts of mercury which is "never touch the DOM directly in application code, all DOM reading and writing goes through node_modules".

It's a core philosophy of mercury that when building FRP-style unidirectional apps, the DOM is an implementation detail and should not leak throughout your application code.

@spion
Copy link
Author

spion commented Sep 16, 2014

Okay, I think all the other points are well covered, but I'm looking at the basic example from the perspective of approachability. I think it would benefit immensely from leaving value-event out.

Lets look at it:

var mercury = require("mercury")
var h = mercury.h

var clicks = mercury.input()
var clickCount = mercury.value(0)

clicks(function () {
    clickCount.set(clickCount() + 1)
})

function render(clickCount) {
    return h("div.counter", [
        "The state ", h("code", "clickCount"),
        " has value: " + clickCount + ".", h("input.button", {
            type: "button",
            value: "Click me!",
            "ev-click": mercury.event(clicks)
        })
    ])
}

mercury.app(document.body, clickCount, render)

Here, to the unsuspecting beginner, clicks can be any special magic value with an elaborate interface which is transformed by mercury.event into something unknown.

Now (if I'm understanding correctly) here is what happens if I remove value-event

var mercury = require("mercury")
var h = mercury.h

var clicks = mercury.input()
var clickCount = mercury.value(0)

clicks(function () {
    clickCount.set(clickCount() + 1)
})

function render(clickCount) {
    return h("div.counter", [
        "The state ", h("code", "clickCount"),
        " has value: " + clickCount + ".", h("input.button", {
            type: "button",
            value: "Click me!",
            "ev-click": function(e) { clicks(); } 
        })
    ])
}

mercury.app(document.body, clickCount, render)

Now the entire interface of clicks is obvious and clear (both the emitter and the listener aspects), and its also clear that a function is expected as a value for "ev-click"

@Raynos
Copy link

Raynos commented Sep 16, 2014

In the future we are going to use handles

var mercury = require("mercury");
var h = mercury.h;

var clickCount = mercury.value(0);
var handles = mercury.handles({
    clicks: function () {
        clickCount.set(clickCount() + 1);
    }
});

function render(clickCount) {
    return h("div.counter", [
        "The state ", h("code", "clickCount"),
        " has value: " + clickCount + ".", h("input.button", {
            type: "button",
            value: "Click me!",
            "ev-click": mercury.event(handles.clicks)
        })
    ]);
}

mercury.app(document.body, clickCount, render);

Here you can no longer put functions in ev-click you must place a handle.

In the future mercury.event : (Handle) => Handle i.e. the mercury.event function is a helper that takes a Handle and returns a Handle.

When you pass a Handle to the rendering function, the rendering function can embed the handle in the vnode and you as an application will know that when a click happens the appropiate method for that handle will be called.

The usage of handles is there to enforce strictness and unidirectionality.

Currently the render function could just call clicks without going through user intent which is unexpected and breaks assumptions.

@Raynos
Copy link

Raynos commented Sep 16, 2014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment