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
- 824