REMOVE THE MAGIC

with

Functional Programming

https://slides.com/davesters/remove-magic-functional-programming

OUR APPLICATIONS

ARE FULL OF MAGIC

  • Large frameworks
  • ORMs
  • Multiple layers of inheritance
  • Dependency Injection
  • Convention over Configuration

HOW DO WE

FIGHT MAGIC?

Level up our magic resistance

SIMPLICITY

The state or quality of being simple

+10 MR

CONVENTION

OVER

CONFIGURATION

ORMs

let results = accounts
    .where({
        first_name: 'Bob',
        last_name: 'Sacameno',
        deleted: NULL,
        ref_id: 2
    })
    .not().andWhere({ other_id: 3 })
    .orderByDesc('created')
    .limit(10);
let results = db.query("SELECT * FROM accounts WHERE
    first_name = 'bob' AND last_name = 'Sacameno' AND
    deleted IS NULL AND ref_id = 2 AND other_id <> 3
    ORDER BY created DESC LIMIT 10");

DEPENDENCY INJECTION

$container->bind('iFooContext', '\Foo\Context');
$container->bind('iBarController', '\Controllers\BarController');
…

AUTOMAPPERS

public class Account {
    public int id;
    public string name;
    public string email;
}
var account = Mapper.Map<Account>(databaseAccount);
CreateMap<Account, DatabaseAccount>()
  .ForMember(m => m.name, a => a.name = a.first_name + " " + a.last_name)
  .ForMember(m => m.email, a => a.email.ToLower);

AUTOMAPPERS

public class Account {
    public int id;
    public string name;
    public string email;

    public static function Map(DatabaseAccount account) {
        var a = new Account();

        a.id = account.id;
        a.name = account.first_name + " " + account.last_name;
        a.email = account.email.ToLower();

        return account;
    }
}
var account = Account.Map(databaseAccount);

"Typing is not the bottleneck"

- Michael Hill?

OBLIGATORY QUOTE SLIDE

SEGWAY

SEGUE

You are responsible for ALL the code in your project

FUNCTIONAL PROGRAMMING

"A style of building computer programs, that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data..."

- Wikipedia

+50 MR

TENETS OF FUNCTIONAL PROGRAMMING

  • First-class functions

  • Pure functions

  • Immutable Data

  • Plenty more...

FIRST-CLASS FUNCTIONS

let add = function(a, b) {
    return a + b;
};

HIGHER-ORDER FUNCTIONS

function getSomeAsyncData('foo', function(data) {
    // do something with async data here.
});

PURE FUNCTIONS

function add(a, b) {
    return a + b;
}

REFERENTIAL TRANSPARENCY

let sum = add(2, 3);

let sum = 5;

IMMUTABLE DATA

let numbers = [1, 2, 3, 4, 5];

for (x = 0; x < numbers.length; x++) {
    numbers[x] = numbers[x] + 5;
}

An immutable object is an object whose state cannot be modified after it is created.

IMMUTABLE DATA

let numbers = [1, 2, 3, 4, 5];

for (x = 0; x < numbers.length; x++) {
    numbers[x] = numbers[x] + 5;
}
let biggerNumbers = numbers.map(function(num) {
    return num + 5;
});

Functional Way

let biggerNumbers = numbers.map(add5);
function add5(a) {
    return a + 5;
}

IMMUTABLE DATA

function add5(a) {
    return add(a, 5);
}

let num = add5(7); // 12
function add5(a) {
    return a + 5;
}
function add(a, b) {
    return a + b;
}

FUNCTION COMPOSITION

TODO APP

LET'S  BUILD SOMETHING...

Index.php

require 'vendor/autoload.php';
require 'src/TodoApp.php';
require 'src/Mustache.php';

$handlers = [];

$mustache = Mustache::Render(new Mustache_Engine(), function($tpl) {
    return file_get_contents(__DIR__ . "/views/$tpl.mustache");
});

$klein = new \Klein\Klein();

$todoApp = new TodoApp($klein, $mustache, $handlers);
$todoApp->setRoutes();
$todoApp->start();

TodoApp.php

class TodoApp {
  private $app, $handlers, $mustache;

  public function __construct(Klein $app, $mustache, $handlers) {
    $this->app = $app;
    $this->handlers = $handlers;
    $this->mustache = $mustache;
  }

  public function start() {
    $this->app->dispatch();
  }

  public function setRoutes() { ... }

  private function handle($view, $handlers) { ... }
}

TodoApp.php routes

public function setRoutes() {
  $h = $this->handlers;

  $this->app->respond('/', $this->handle('index', $h['index']));
}

private function handle($view, array $handlers) {
  // Loop over $handlers array and build view model
    
  $this->render($this->mustache($view, $model));
}

IndexHandler.php

class IndexHandler {

  public static function Handle($dataSource) {
    return function($model, $params) {
      $todos = $dataSource('SELECT id, todo, completed
        FROM todos WHERE deleted IS NULL ORDER BY created');
    
      return [
        'title' => 'Todo App',
        'todos' => $todos,
        'count' => count($todos)
      ];
    };
  }

}
$handlers = [
  'index' => [ Handlers\IndexHandler::Handle($dataSource) ]
];

Add and Update Handlers

$handlers = [
  'index'  => [ Handlers\IndexHandler::Handle($dataSource) ],
  'add'    => [ Handlers\AddTodoHandler::Handle($dataSource) ],
  'update' => [ Handlers\UpdateTodoHandler::Handle($dataSource) ]
];

IndexQuery.php

class IndexQuery {

  public static function Query($dataSource) {
    return function($model, $params) {
      $todos = $dataSource('SELECT id, todo, completed
            FROM todos WHERE deleted IS NULL ORDER BY created');

      return [ 'todos' => $todos ];
    };
  }

}

IndexHandler.php

class IndexHandler {
  public static function Handle() {
    return function($model, $params) {
      return [
        'title' => 'Todo App',
        'todos' => $model['todos']
        'count' => count($model['todos']
      ];
    };
  }
}
$handlers = [
  'index' => [
    Queries\IndexQuery::Query($dataSource),
    Handlers\IndexHandler::Handle()
  ],
  ...
];

Summary

  • What problem am I having that this library is going to solve?
  • Can I change my problem around so I don’t need it?

Thanks

Sample App:

https://github.com/davesters/functional-todo-example


"8 Lines of Code" Keynote:

http://www.infoq.com/presentations/8-lines-code-refactoring


Contact:

Twitter - @davesters

Github - davesters

Website - http://www.lovesmesomecode.com

Remove the magic with functional programming

By David Corona

Remove the magic with functional programming

  • 815