How to Make Front-End

More Functional

Julia Gao

@ryoia

{ "firstName"  : "Julia"
, "lastName"   : "Gao"
, "profession" : "Software Developer"
, "company"    : "O.C. Tanner"
, "likesMath"  : true
, "language"   : "JavaScript"
, "likesJS"    : Maybe
, "twitter"    : "@ryoia"
}

- Functional Programming Concepts

 

- Maybe Types

 

- JavaScript alternatives

What The Functional?

  • Immutable Data

 

  • Pure Functions

 

  • Composable Functions
var unchangeable = 2;

unchangeable = 3; // oh hell no

But ... doesn't every variable get changed?

Is mutable data good? Or just easy?

- No control over parameters

- No control over outputs

- Any changes on the same variables can produce bugs

- Not thread safe

 

- No reference sharing (more complexity)

It is so easy to ...

var scores = [90, 78, 100, 94, 80];

scores.concat(86);

But ... isn't copying everything slow?

Clojure(Script), Haskell(PureScript), Erlang...

Persistent Data Structure

Ok so it is not necessarily inefficient...

- No more locking on multi-thread operations

- Copy === Create a new reference (constant time)

- Value comparison === reference comparison

- Data consistency!

 

- Provides peace of mind

For/While Loops

function factorial(n) {
   if (n === 0) return 1;
   else {
      return n * factorial(n - 1);
   }
}
function factorial(n) {
   var result = 1;
   for (var i = 1; i <= n; i++) {
      result *= i;
   }
   return result;
}

Loops encourage mutation

Pure Functions

Same input, same ouput

10 / 2

 

|-6|

 

10^2

Benefits

- Get expected results, every single time

 

- Easy to refactor

 

- Better organized code base

 

- Easier to bring people on board

split, slice, toLowerCase, toUpperCase, Math[...], etc...

JavaScript has pure functions...Lots!

var programs = [
  ...
];

function programChecker(program) {
  if (program && program.name && programs.indexOf(program.name) > -1) {
    return true;
  }
  return false;
}

Composable Functions

°

f ° g

f(g(x))

var foo = 'bar';

foo.toUpperCase().split('A').concat(['foo']);
//["B", "R", "foo"]
// not pure, mutable functions
var x = 'this sentence can be changed';
function mutation(str) {
  if (x.indexOf(str) > -1) {
    x = 'oops';
  }
}

x.toUpperCase().indexOf('CHANGED'); // 21

mutation('sentence');

x.toUpperCase().indexOf('CHANGED'); // -1
// pure functions

var x = 'hopefully this sentence will not be changed';

function newCreation(str) {
  if (x.indexOf(str) > -1) {
    return 'oops';
  } 
  return x;
}

x.toUpperCase().indexOf('CHANGED'); // 36

newCreation('sentence'); // oops

x.toUpperCase().indexOf('CHANGED'); // 36
function getDetails(group) {
  group.exactId = group.id ? group.id : 123;

  app.sendRequest('/url' + group.exactId, function(err, details) {

    if (details.images && details.images !== '') {
      group = imageProcesser(details.images, group);
      
      return [details, group];
    } else {
      return 'images not found';
    }
  });
}

function imageProcessor(images, group) {
  if (images && images.length > 0) {
    ...
    group.imgs = images;
  }

  return group;
}
function getDetails(group) {
  var id = group.id ? group.id : 123;

  app.sendRequest('/url/' + id, function(err, details) { 

    if (details.images && details.images !== '') {
      var processed = imageProcessor(details.images);

      return [details, processed];
    } else {
      return 'images not found';
    }
  });
}

function imageProcessor(images, group) {
  if (images && images.length > 0) {
    ...
    return Object.assign({}, group, {imgs: images});
  }

  return group;
}

Types!

Every expression has types!

Your code cares about types...

using System;

public class Test
{
    public static void Main()
    {
        Console.WriteLine("Hello" * 1);
    }
}

error CS0019: Operator `*' cannot be applied to operands of type `string' and `int'

'1' * 2 -> 2

'hi' * 10 -> NaN

Even JavaScript cares about types...sort of

function checkAllTheThings(shouldBeNumber) {

  if (isNaN(shouldBeNumber)) { return null; }

  if (typeof shouldBeNumber === 'number') { ... }
}

Unit Test Much?

Don't you want to just ... not do that?

module WordNumber where

import Data.List (intercalate)

digitalToWord :: Int -> String
digitalToWord n
  | n == 1 = "One"
  | n == 2 = "Two"
  | n == 3 = "Three"
  | n == 4 = "Four"
  | n == 5 = "Five"
  | n == 6 = "Six"
  | n == 7 = "Seven"
  | n == 8 = "Eight"
  | n == 9 = "Nine"
  | otherwise = "wrong"

digits :: Int -> [Int]
digits n = count n []

count :: Int -> [Int] -> [Int]
count n c
  | div n 10 /= 0 = count (div n 10) (combine n c)
  | otherwise = (combine n c)

combine :: Int -> [Int] -> [Int]
combine x ys = [mod x 10] ++ ys

wordNumber :: Int -> String
wordNumber n = intercalate "-" $ map digitalToWord (digits n)

JavaScript Alternatives?

ImmutableJS

const regularMap = new Map()

regularMap.set(1, 'hi')
// Map { 1 => 'hi'}

regularMap.set(2, 'bye')
// Map { 1 => 'hi', 2 => 'bye'}
const immutableMap = Immutable.Map({1: 'hi'})

const changedMap = immutableMap.set({2: 'bye'})

// immutableMap.size -> 1
// changedMap.size -> 2

ClojureScript

(def v [1 2 3]) -> [1 2 3]

(conj v 4) -> [1 2 3 4]

v -> [1 2 3]
(defn increment [person]
  (assoc person :age (+ 1 (:age person))))

PureScript

module Add where

import Control.Monad.Eff.Console (log)

main = log "An addition function!"

add :: Int -> Int -> Int
add x y = x + y
type Person = { name :: String
              , age :: Int
              }

increment :: Person -> Person
increment p = p { age = p.age + 1 }

Haskell: Purely functional language, typed,
promote modular functions
PureScript: Compiles to JS (easy to integrate),
fast growing community

 

Clojure: JVM (easy to integrate), not typed,
promote modular functions
ClojureScript: Compiles to JS, engaging community

 

F#: Typed, C# -> F#

Conclusion

- Immutable data structure is more efficient

 

- FP produces more reliable code

 

- FP makes us think more before coding

Thank you!

 

http://slides.com/ryoia/fp-js#/

 

Julia Gao

@ryoia

strange-loop-fp-js

By ryoia

strange-loop-fp-js

  • 1,638