Алексей Распопов
Вы уже знакомы с конвеерами?
$ cat file.txt | grep ^boo | foo
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']
// VanillaJS
fn.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);
});
});
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);