Функциональное реактивное программирование
DESCRIPTION
Выступление Дмитрия Кириенко и Алексея Осипенко на ноябрьской встрече Донецкого Лямбда-клуба. http://l.dn.ua/ http://dimoneverything.blogspot.com/2013/11/blog-post.htmlTRANSCRIPT
Javascript unleashed
Функциональное программирование объединяет гибкость и мощь абстрактной математики с интуитивной понятностью абстрактной математики
function Counter(element) { var that = this; this.count = 0; this.clicked = function () { this.count += 1; } $(element).click(function() { return that.clicked(); });};
$("#login").on("click", function (event) {
var value = $(event.target).val();
if (value.length > 0) {
$("#notice").text(value);
}
});
var clickE = $("#login").events("click");clickE.onValue(function (event) { var value = $(event.target).val(); if (value.length > 0) { $("#notice").text(value); }});
$("#login").on("click", function (event) {
var value = $(event.target).val();
if (value.length > 0) {
$("#notice").text(value);
}
});
var clickE = $("#login").events("click");var values = clickE.map(function (event) { return $(event.target).val();});var nonEmptyValues = values.filter(function (value) { return value.length > 0;});nonEmptyValues.onValue(function (value) { $("#notice").text(value);});
$("#login").on("click", function (event)
{
var value = $(event.target).val();
if (value.length > 0) {
$("#notice").text(value);
}
});
var values = $("#login").events("click") .map(function (event) { return $(event.target).val();}).filter(function (value) { return value.length > 0;});
values.onValue(function (value) { $("#notice").text(value);});
$("#login").on("click", function (event) {
var value = $(event.target).val();
if (value.length > 0) {
$("#notice").text(value);
}
});
var values = $("#login").events("click") .map(chain(pluck('target'), $, method('val')) .filter(nonEmpty);
values.onValue(function (value) { $("#notice").text(value); });
$("#login").on("click", function (event)
{
var value = $(event.target).val();
if (value.length > 0) {
$("#notice").text(value);
}
});
type Stream[a] = [(T, a)]type Box[a] = T -> a
Stream will receive B and C
Box immediately receives A and will receive B and C
A B C
A B C
new Stream( function (sink) {
sink(new Event(10));
sink(new Event(20));
return function() {};
});
Box.nothing(); // empty boxBox.unit(10); // box with constant value of 10Stream.never(); // empty streamStream.unit(10); // stream with immediate value of 10
Stream.error(10); // stream with immediate error of 10Box.error(10); // box with constant error of 10
Stream.later(1000, 10);// in a second pops out 10
$('input').stream('keyup');
// stream with keyup events
asyncCall(data, function (response) { // You have response from your asynchronous call here.}, function (err) { // if the async call fails, the error callback is invoked});
asyncCall(data,function(response){ // You have response from your asynchronous call here.}, function(err){ // if the async call fails, the error callback is invoked});
var promise = asyncCall(data);
promise.done(function(response){
// You have response from your asynchronous call here.
}).fail(function(err){
// if the async call fails, you have the error response
here.
});
Stream.fromPromise($.ajax(params));
// pops a success event on successful response
// or the error event on error response
_.map([1, 2, 3], function (x) { return x*x; });
// returns [1, 4, 9]
_.map([1, 2, 3], function (x) { return x*x; });
// returns [1, 4, 9]
priceValue.map(function(x) { return x > 1000;});
priceValue.filter(function(x) { return x >= 1000;});
_.flatMap(users, function (user) { return user.comments(); });
// returns flat list of all the comments the users have
var requests = usernames.map(function(u) { return { url: "/check-username/" + u; }});requests.flatMap(function (params) { return Stream.fromPromise($.ajax(params));});// usernames => "John", "Peter"// requests => {"url": "/check-username/John"}, ...// flatMap => {correct: true}, {correct: false}
Stream.prototype.map = function (f) { return this.flatMap(function (x) { return Stream.unit(f(x)); });}
Stream.prototype.filter = function (f) { return this.flatMap(function (x) { if f(x) { return Stream.unit(x); } else { return Stream.nothing(); } });}
password.map2(passwordConfirmation, function(l, r) { return l == r; });
Box.prototype.map2 = function (other, f) { return this.flatMap(function (x) { return other.map(function (y) { return f(x, y); }); });}
Box.prototype.apply = function (other) { return this.map2(function (f, x) { return f(x); } );}
var id = function (x) { return x; }
Stream.merge = function (streams) {
return Stream.fromList(streams).flatMap(id);
}
var inc = plusClicks.map(function() { return 1; });var dec = minusClicks.map(function() { return -1; });var change = plus.merge(minus);
Stream.prototype.debounce = function (delay) { return this.flatMapLast(function (value) { return Stream.later(delay, value); });}
Box.prototype.sampledBy = function (sampler) { var that = this; return sampler.flatMap(function () { return that.take(1); });}
votes.take(10);
hp.takeWhile(function (hp) { return hp > 0; } );
mousemove.takeUntil(escapePressed);
потоки событий мыши
mousedown = $(document).stream('mousedown', '.item')mouseup = $(document).stream('mouseup')mousemove = $(document).stream('mousemove')
mousedrag = mousedown.flatMapLast( (e)-> mousemove.takeUntil(mouseup))
позиции курсора
cursorPosition = mousemove.box().map( (e)-> { x: e.clientX, y: e.clientY })
startPosition = cursorPosition.sampledBy mousedowncurrentPosition = cursorPosition.sampledBy mousedrag
# cursorPosition → { x: 112, y: 234 }
shiftPosition = startPosition.zip(currentPosition) .map((s, m)-> { left: m.x-s.x, top: m.y-s.y } )
shiftPosition.onValue( (pos)-> $('.item').css(pos) )
mousedown.onValue (e)-> e.preventDefault()
newPosition = mousedown.flatMapLast (md)-> target = $(md.target); {left, top} = target.offset(); [startX, startY] = [md.clientX - left, md.clientY - top] mousemove.map (mm)-> target: target left: mm.clientX - startX top: mm.clientY - startY .takeUntil mouseup
чо?