User Interface
as a pure function

  • State machine

  • Magical Powers

  • Pure Function

  • Virtual DOM (pt. 2)

  • Immutability(pt. 2)

A pure function of an application state

inspired from React

Benefit of using a pure function for UI

  • Works on top of immutability
  • Works on top of virtual DOM
  • We can save, load and undo the UI

Let's define a pure function

To be pure a function needs to meet two criteria:

  • Evaluates the same result value given the same argument value(s)
  • Evaluation of the result does not cause any semantically observable side effect 

A simple example

Let's take the toUpperCase method of the String object

var str = "uppercase";
str.toUpperCase() // <= UPPERCASE
str               // <= uppercase

Let's try it with an actual UI

If we have this...

<span id="counter">0</span>
<input id="color" type="color"></input>
<button id="increment"></button>

<script>
 $('#color').on('click', function () {
     $('#colored-counter').css('color', this.value);
 })

 $('#increment').on('click', function () {
     var oldValue = $('#counter').html();
     var newValue = 1 + Number(oldValue);
     $('#counter').html(newValue);
 })
 </script>
<span id="counter">0</span>
<input id="color"></input>
<button id="increment"></button>

<script>
 var state = {color: '', value: 0};

 function updateUI() {
     $('#counter').css('color', state.color);
     $('#counter').html(state.value);
 }

 $('#color').on('keyup', function () {
     state.color = this.value;
     updateUI();
 })

 $('#increment').on('click', function () {
     state.value++;
     updateUI();
 })
</script>

...it could be written as:

This is how first variant works:

<span id="counter">0</span>
<input id="color"></input>
<button id="increment"></button>

<script>
 $('#color').on('keyup', function () {
     $('#counter').css('color', this.value);
 })

 $('#increment').on('click', function () {
     var oldValue = $('#counter').html();
     var newValue = 1 + Number(oldValue);
     $('#counter').html(newValue);
 })
 </script>

This is how second one does:

<span id="counter">0</span>
<input id="color"></input>
<button id="increment"></button>

<script>
 var state = {color: '', value: 0};

 function updateUI() {
     $('#counter').css('color', state.color);
     $('#counter').html(state.value);
 }

 $('#color').on('keyup', function () {
     state.color = this.value;
     updateUI();
 })

 $('#increment').on('click', function () {
     state.value++;
     updateUI();
 })
</script>

Think about scaling the first version

Think about scaling the second version

The complexity index

  • O(N*M)
  • O(N+M)

Magical Powers

// save
var cannedState = deepcopy(state);

// load
state = cannedState;
updateUI();
<span id="time-pos"></span>
<button id="back">Back</button>
<button id="next">Next</button>

<script>
var time = {history: [], pos: 0};

function updateTimeUI() {
    $('#time-pos').html('Position ' + time.pos + ' of ' + time.history.length);
}

function saveState() {
    time.history.push(deepcopy(state));
    time.pos++;
    updateTimeUI();
}

$('#back').on('click', function () {
    // Move history pointer
    time.pos--;
    updateTimeUI();
    // Load historic state
    state = deepcopy(time.history[time.pos]);
    updateUI();
})
$('#next').on('click', function () {
    time.pos++;
    // ... same
})

// ...

function updateUI() {
    // Save state to history on every change
    saveState();
    // ... continue as usual
}
</script>
<div id="ui">
    <span id="count">2</span>
    <ul>
        <li>hi</li>
        <li>there</li>
    </ul>
    <button id="add"></button>
</div>

<script>
var state = {items: ['hi', 'there']}

function updateUI() {
    $('#count').html(state.items.length);
    // Compare ul.childNodes to state.items and make updates
    // ...
}

$('ul').on('click', 'li', function () {
    state.items.splice($(this).index(), 1);
    updateUI();
})

$('#add').on('click', function () {
    state.items.push(getNextString());
    updateUI();
})
</script>

Impure Function version

<div id="ui">
    <span id="count">2</span>
    <ul>
        <li>hi</li>
        <li>there</li>
    </ul>
    <button id="add"></button>
</div>

<script>
var state = {items: ['hi', 'there']}

function render(state) {
    var span = '<span id="count">' + state.items.length + '</span>';
    var lis = state.items.map(function (item) {
        return '<li>' + item + '</li>';
    });
    return span + '<ul>' + lis.join('') + '</ul>'
}

function updateUI() {
    $('#ui').html(render(state));
}

$('ul').on('click', 'li', function () {
    state.items.splice($(this).index(), 1);
    updateUI();
})

$('#add').on('click', function () {
    state.items.push(getNextString());
    updateUI();
})
</script>

Pure Function version

User Interface as a pure function

By elismatic

User Interface as a pure function

  • 693