Алексей Распопов
Вы уже знакомы с конвеерами?
$ cat file.txt | grep ^boo | foo
Нам нужен какой-нибудь способ соединять программы как садовые шланги — вкручивать новые сегменты когда необходимо обрабатывать информацию по-другому. Это так же применимо к I/O.
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, ...];
numbers.map((n) => n * n).filter((n) => n % 2 === 0).reduce((a, b) => a + b);
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, …];
numbers.map((n) => n * n).filter((n) => n < 40).filter((n) => n % 2 === 0).reduce((a, b) => a + b);
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, …], sum = 0, num;for(var i = 0; i < numbers.length; i++){num = numbers[i];num *= num;if(num % 2 === 0){sum += num;}}
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, …], sum = 0, num;for(var i = 0; i < numbers.length; i++){num = numbers[i];num *= num;if(num % 2 === 0){sum += num;}}
numbers.map(square).filter(isEven).reduce(sum);
Чистая функция (англ. pure function) — функция, которая:
Побочный эффект функции (англ. side effect) — возможность функции в процессе выполнения:
Math.random, Date Неизменяемые данные (англ. immutable) — данные, которые программа не может модифицировать в процессе своей работы.
Динамическое состояние — зло.
var array = [];array.push(data);array; // [data]
var array = Immutable.List([]);var newArray = array.push(data);array; // []newArray; // [data]
ImmutableJS — библиотека с набором неизменяемых структур данных. Разрабатывается в Facebook.
Функция высшего порядка (англ. higher-order function) — функция, принимающая в качестве аргументов другие функции или возвращающая другую функцию в качестве результата.
Классические примеры: map, filter, reduce
Так же называют операторами (термин из математики)
Классический пример — функции для работы с массивом.
map(parseInt, ['1', '123', '4'])filter(function(n){ return n > 100; }, [1984, 234, 1]);reduce(max, [...]);
function property(key, target){return target[key];}
property('length', [1, 2]); // 2
var listOfUsers = [{name: 'Ann'}, {name: 'Max'}, ...];map(function(target){return property('name', target);}, listOfUsers);
Частичное применение (англ. partial application) — процесс фиксации части аргументов функции, который создает другую функцию с меньшим количеством аргументов.
var username = partial(property, 'name');username({name: 'Alex'}); // 'Alex'map(username, listOfUsers);// [{name: 'Ann'}, {name: 'Max'}] -> ['Ann', 'Max']
// VanillaJSfn.bind(context, arg1, arg2, ...);
// UnderscoreJS_.partial(fn, arg1, arg2, ...);
Каррирование (англ. currying) — преобразование функции от многих аргументов в функцию, берущую свои аргументы по одному.
var property = curry(function(key, target){return target[key];});
var duration = property('duration');// (target) => target['duration'];duration({ duration: 10, ... }); // 10
property('name', {name: 'Sam'}); // 'Sam'
Композиция (англ. composition) — процесс применения одной функции к результату другой.
fn1(fn2(value));
| Один | Много | |
|---|---|---|
| Синхронно | Композиция | Итератор |
| Асинхронно | Promise | Observable |
Прежде чем писать код — выбери правильный инструмент!
function compose(f, g){return function(a){return f(g(a));};}
function compose(f, g){return function(){return f(g.apply(this, arguments));};}
Haskell:
fn1 >>= fn2 >>= fn3
unit(value).bind(fn1).bind(fn2);
Не путать оператор bind с методом Function::bind

nullsafe(data, 'company.description').bind(toLowerCase).bind(translate).bind(alert);
Just(5).bind(square).bind(isEven).bind(alert);
function isEven(number){return number % 2 ? Nothing() : Just(number);}
Nothing().bind(alert); // иии ничего не происходит
function Just(value){return value && value.isMonad ? value : {isMonad: true,bind: function(morphism){return Just(morphism(value));}};};
Бесплатно, без СМС: github.com/alexeyraspopov/maybe
getUserInfo(function(error, info){if (error) throw error;getUserTweets(info, function(error, tweets){if (error) throw error;sendToEmail(tweets);});});
События — плохой выбор для управления потоком данных. Они требуют распространения изменяемых (mutable) данных по вашему коду, и это не идиоматически или принятно для потока данных.
getUserInfo().then(getUserTweets).then(sendToEmail);
Функциональное Реактивное Программирование (англ. functional reactive programming) — парадигма, ориентированная на потоки данных и распространение изменений.
Event Stream, он же Observable — источник сообщений.
Promise + Итератор
document.addEventListener('keydown', function(event){if(event.keyCode >= 37 && event.keyCode <= 40){// Механика игры здесь}});
$(document).asEventStream('keydown').filter(isArrow).map(selectDirection).onValue((direction) => /* Что-то происходит */)
var draggingDeltas = startDrag.flatMap(function() {return html.asEventStream('mousemove').map(xyFromEvent).slidingWindow(2,2).map(getDelta).takeUntil(endDrag)})
// Один, синхронноmaybe(user.bio).bind(prettyBio).bind(output);
// Один, асинхронноfetch('/user').then(tweetsByUser).then(filteredRetweets);
// Много, синхронноnumbers.filter(isEven).map(square).reduce(sum, 0);
// Много, асинхронноevent('keydown', document).filter(isArrow).map(coords).subscribe(render);