LINUX.ORG.RU

Реактивное програмирование


0

4

Прочитал про сабж и вот этот пример:

К примеру, в императивном программировании присваивание a := b + c будет означать, что переменной a будет присвоен результат выполнения операции b + c, используя текущие (на момент вычисления) значения переменных. Позже значения переменных b и c могут быть изменены без какого-либо влияния на значение переменной a. В реактивном же программировании значение a будет автоматически пересчитано, основываясь на новых значениях.

натолкнул на мысль. А ведь это подмена понятий. Тут a - это же просто фунция! Что-то вроде

b=1
c=1
a=function(){return b+c}
a() -->2
b=2
a() -->3
[update]

Даже вот так наглядней будет, чтобы не отвлекаться на присваивания:

b=function(){return 1}
c=function(){return 1}
a=function(){return b()+c()}
a() -->2
b=function(){return 2}
a() -->3

[/update]

ведь это же обычное императивное программирование. Дело в синтаксисе? То что мы обращаемся к a не a(), а просто: a? Это важно, дооо.

Тем более странно, что там снизу, на вики, они плетут еще что-то пр фп-реактивное программирование. Да современное фп, со своей анальной фиксацией на замыканиях и иммутабельности, это 100% АНТИреактивное программирование. Они наоборот замораживают состояние, какая там реактивность может быть? Что это за идиотизм такой? Как разобраться в этих дебрях, когда кругом идиотизм зашкаливает:)?

PS Да, и кстати, эта парадигма нам как-бы, в очередной раз, намекает, что никакой разницы между функциями и данными нет, BTW.



Последнее исправление: anonimous (всего исправлений: 3)
Ответ на: комментарий от anonymous

Не всякая ФВП функция обязанна быть каррированной. Строго говоря, ФВП, чтобы быть таковой, достаточно быть first-class

anonimous
() автор топика
Ответ на: комментарий от anonimous

Мне надоело твое манипулирование терминами.

поэтому ФП (в понимании haskell-like) с декларативностью не связано

ФП (в понимании haskell-like)

Ты считаешь что ФП это - haskell и haskell-подобные языки.

А вот что такое ФПР, и как оно может быть вообще, существовать, вот это интересно, потому как ящетаю, что это в принципе невозможно.

Ты не понимаешь, как можно совместить ФП и РП.

Вот РП на haskell (это ведь haskell-подный язык, верно?): http://www.haskell.org/haskellwiki/Functional_Reactive_Programming

Kuzy ★★★
()
Ответ на: комментарий от anonimous

Не всякая ФВП функция обязанна быть каррированной

покажи пример такой функции не каррированной

anonymous
()
Ответ на: комментарий от Kuzy

манипулирование терминами.

Это не манипулирование терминами. В старые годы, ФП называли любой ЯП с ФВП. Сейчас же под этим понимается, как минимум ФВП+иммутабельность+лексические замыкания. Просто уточняю, чтобы не было путаницы.

Вот РП на haskell

Ты предлагаешь мне изучить заскель, птом перелопатить эту реализацию, чтобы убедиться в очевидном — ничего кроме сахара от РП там нет? Я и так догадываюсь, спасибо.

anonimous
() автор топика
Ответ на: комментарий от anonimous

А вот что такое ФПР, и как оно может быть вообще, существовать, вот это интересно, потому как ящетаю, что это в принципе невозможно.

потому как ящетаю, что это в принципе невозможно.

Возможно. Я указал на реализации которые удовлетворяют твоим требованиям.

Kuzy ★★★
()
Ответ на: комментарий от arturpub

Че то запарился я с Вашим примером. Я не догнал сразу, что тут кто-то должен вносить изменения вручную, сами то данные не установяться, глупо было бы. Отсутствие ошибок не гарантирую, но смысл, думаю такой.


data={}

set_number=function(n){data.number=n; upgrade(); show()}
set_price=function(n){data.price=n; upgrade(); show()}
set_sum=function(n){data.sum=n; upgrade(); show()}

show=function(){
   if(!(data.price&&data.number&&data.sum))return;
   console.log("number: "+data.number); console.log("price: "+data.price); console.log("sum: "+data.sum)
}

upgrade=function(){
   if(data.sum&&data.price)data.number=data.sum/data.price
   if(data.sum&&data.number)data.price=data.sum/data.number
   if(data.price&&data.number)data.sum=data.price*data.number
}

set_number(2)
set_price(10)

console.log("\n")

set_sum(30)

console.log("\n")

set_price(20)

--------------\  
number: 2
price: 10
sum: 20


number: 3
price: 10
sum: 30


number: 1.5
price: 20
sum: 30

На функцию show, можно повесить перирисовку таблицы, если нужны табличные данные. А так, вся фишка этого_вашего_РП — просто этот upgrade после каждого изменения, происходит под капотом. И, да, это обычная императивщина, и, на ФП, по понятным причинам, такое не напишешь.

anonimous
() автор топика
Ответ на: комментарий от anonimous

Даже set_number set_price set_sum можно переименовать в просто number price и sum для наглядности.

anonimous
() автор топика
Ответ на: комментарий от anonimous

Вашим

Слушай, мы уже кусались, можно и на ты.

Проблема в том, что при увеличении количества компонент будет расти количество && в условиях апгрейда, а при более сложных реакциях (у нас тут кольцо, а могут быть выходы из кольца, как случае ндс в составе суммы — будет в общем виде к-л граф пересчетов), при более сложных ты начнешь затирать только что введенные данные, не зная естественной схемы поведения. Чтобы такое решать декларативно, движок должен запоминать, куда обращалась какая фомула (а их может быть для компонента более одной), и довычислять остальные исходя из истории присвоений, отдавая предпочтение сохранности недавно установленных значений, затирая старые конфликтные, а не новые.

По поводу фп/фрп я вовсе не ратую, т.к. это баззворд, и я честно говоря плохо понимаю, что под этим имеют ввиду в общем. Мой посыл в том, что прямая реализация в декларативном виде не может не иметь неявной модели пересчетов, а прямая императивная может быть слишком сложной для отладки всех случаев, т.к. их колво равно колву перестановок компонентов во времени. Поэтому и нужно в движке отделять данные от реакции, чтобы во-первых разрывать кольца и видеть выходы, а во-вторых избегать зацикленности пересчетов при округлении. Представь, что тебе отгрузили вагон за полтора ляма, а в нем 1234.567 кг, цена не сойдется при обратном пересчете и будет вечно вызывать реакцию кольца, если ее не разрывать по временным приоритетам.

Это все к тому, что пересчет это процесс, но его можно завернуть в декларативный вид только при наложении нужных общих правил, и на простую математику это не ложится. Процесс со своей логикой, /отдельный/ от данных. Эксель это решил тем, что тупо нельзя создавать кольца — все вычисления строго древовидны, например.

Как это ложится на «фп», я хз, это лишь баззворд без конкретных деталей активной схемы.

arturpub ★★
()
Ответ на: комментарий от anonimous

Именно так. Как конкретно ты будешь реализовывать реакции на событийный поток — псевдоимперативно или псевдофункционально — неважно. И тот и другой мир одинаков по возможностям вычислений, только подходы разные; в фп-подходе одно упрощается, а в императивном другое. Мыслить черно-белым просто глупо и по-адептски, и то и другое симулирует модель в конечном счете, неважно как именно.

arturpub ★★
()
Ответ на: комментарий от anonimous

Да, еще одно забыл. В текущем виде этот кусок все равно страдает от пересчета всего мира, т.к. выходы из кольца могут вести в другие места и при сильной формульной связности ты пересчитаешь всю базу, т.к. невозможно отличить старую транзакцию пересчетов от новой (ты различаешь только неинициализированность). Выходит придется либо вообще все данные апгрейдить, либо рекурсивно вызывать апгрейды для выходов (сейчас после присвоения в апгрейде ничего не происходит, а нужный порядок в общем виде неизвестен в силу недостаточности/отсутствия модели). Суть рп подхода в том, чтобы дернуть только нужное и на этом остановиться.

arturpub ★★
()
Ответ на: комментарий от anonimous

Ты молодец, что начал понимать что-то в этом мире, только вот ты забыл одно - нет никаких «парадигм» в текущих реалиях, кроме «сишной». Любая псевдопарадигма - это эмуляция, и вся та байда, о которой курлычит википедия и всякие бомжи - это жалкая эмуляция и ничего более.

Все эти языковые конструкции - сахар, а весь синтаксис - просто мусор, который придумывается от попа и может быть каким угодно. Правда на лиспе ничего сложнее хелворда не напишешь - причина синтаксис. Поэтому всё, что сложнее хелворда скрывается в реализации.

Т.е. вся эта функциональщина - это попытки скрыть ущербность придуманной функциональной концепции. Все правила и ограничения - всё создано для сокрытия ущербностей. ЯП состоят из сахара на 99% тоже из-за этого.

на самом деле, в любом императивном ЯП

Бредня, такой язык только один - машинное представление и язык который максимально его представляет в «языковых» конструкциях и возможностях - си.

Есть аксиома - всё говно выражается в си - т.е. все парадигмы, все представления, вся эта мура - это возможности си(императивщины), на которой строятся все эмуляторы. Поэтому всё в текущем мире программирования описывается сишкой.

Поэтому ты написал правильно - реактивное «программирование» - это в примитивном случае твои функции(указатели). Но жопа там не в этом.

Устройство там примитивное:

a = 2;//хранит метаинфу зависимостей
b = 3;//хранит метаинфу зависимостей
f(a, b);//тут мы записываем в метаинфу перменной а и б то, что он них зависит функция f().
a = 3;//тут просто апдейтятся все зависимости записанные в переменную а.

В примитиве на сишке:

void f(int a, int b) { fprintf(stderr, "%d, %d\n", a, b);}

typedef struct {
  typeof(f) * fun;
  int * arg_a, * arg_b;
} fun_dump_t;

fun_dump_t dump_fun(typeof(f) fun, int * arg1, int * arg2) {
  return (fun_dump_t){fun, arg1, arg2};
}

typedef struct {
  int value;//у меня инты, у вас там просто var, ибо типов нет
  int dep;
  fun_dump_t dump;//тут конечно надо сделать список функций, но мне лень
} var_t;

var_t init_value(int value) {
  return (var_t){value, 0, (fun_dump_t){}};
}

void set_value(var_t * var, int value) {
  var->value = value;
  if(var->dep)
    var->dump.fun(*(var->dump.arg_a), *(var->dump.arg_b));//тут тоже вызов этого списка.
}

void set_dep(var_t * var, fun_dump_t fun) {
  *var = (var_t){var->value, 1, fun};
}


int main(void) {
  var_t a = init_value(5);
  var_t b = init_value(3);
  set_dep(&a, dump_fun(f, &a.value, &b.value));//добавляем функцию f() в метаинфу переменно b.

  set_value(&a, 1);
  set_value(&b, 2);
  set_dep(&b, dump_fun(f, &b.value, &b.value));//добавляем функцию f() в метаинфу переменно a.
  set_value(&a, 3);
  set_value(&b, 4);
}
Выхлоп:
1, 3
3, 2
4, 4

Так это реализовано, а примитивные случаи, которые ты описал - это указатели, которые итак есть в си.

Carb_blog2
()
Ответ на: комментарий от Carb_blog2

Парадигмы не перестают существовать от того, что ты реализуешь их на Си. Если бы парадигм не было, тебе нечего было бы реализовывать. Такие дела.

anonymous
()
Ответ на: комментарий от anonimous

Лучше уж так (хотя тоже говно):

function variable(val) {
  var observers = [];
  var value = val;

  return {
    get value() {
      return value;
    },

    set value(val) {
      value = val;
      observers.forEach(function (observer) {
        observer.propagate();
      });
    },

    subscribe: function (observer) {
      observers.push(observer);
    }
  };
}

function assign() {
  var expr = arguments[0];
  var args = Array.prototype.slice.call(arguments, 1);
  var observers = [];
  var value;

  var ret = {
    get value() {
      return value;
    },

    propagate: function () {
      value = expr.apply(null, args.map(function (arg) {
        return arg.value;
      }));

      observers.forEach(function (observer) {
        observer.propagate();
      });

      return value;
    },

    subscribe: function (observer) {
      observers.push(observer);
    }
  };

  args.forEach(function (arg) {
    arg.subscribe(ret);
  });

  value = expr.apply(null, args.map(function (arg) {
    return arg.value;
  }));

  return ret;
}

function observe(thing, handler) {
  var ret = {
    propagate: function () {
      return handler.call(null, thing.value);
    }
  };

  handler.call(null, thing.value);

  thing.subscribe(ret);

  return ret;
}

function example() {
  var x = variable(5);
  observe(x, function (value) {
    console.log('x is set to', value);
  });

  var y = variable(7);
  observe(y, function (value) {
    console.log('y is set to', value);
  });

  var z = assign(function (x, y) {
    return x + y;
  }, x, y);
  observe(z, function (value) {
    console.log('"(x + y)" evaluates to', value);
  });

  var w = assign(function (x, z) {
    return x * z;
  }, x, z);
  observe(w, function (value) {
    console.log('"x * (x + y)" evaluates to', value);
  });

  console.log('');

  console.log('=================================');
  console.log('Manually setting x to', 3);
  x.value = 3;
  console.log('');

  console.log('=================================');
  console.log('Manually setting y to', 10);
  y.value = 10;
  console.log('');
}

Ты запаришься вручную обновлять все «переменные», если у тебя их будет больше десятка.

theNamelessOne ★★★★★
()

Охохо, вот теперь я вижу что ты вообще не шаришь. Ты мыслишь в каких древних парадигмах. Сейчас они лишь кирпичик для реализации разных разновидностей параллельных программ. И функциональные кирпичики местами подходят куда лучше. А вообще реактивное программирование похоже на Go. Я бы даже сказал что эта идея лежит в основе основ этого языка.

anonymous
()
Ответ на: комментарий от anonymous

А вообще реактивное программирование похоже на Go. Я бы даже сказал что эта идея лежит в основе основ этого языка.

Не можешь развернуть мысль?

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

Пока сам разбираюсь. Но постараюсь объяснить. Есть горутины и есть каналы. Горутины автоматом берут из каналов данные, обрабатывают и кладут если надо в следующие каналы. Грубо при поступлении каких то данных в каналы они автоматом идут дальше.

Горутина1 -> канал1 -> горутина2 -> канал2 -> ... -> горутинаN -> каналN.

Причем каналы могут быть буферизированные, т.е. можно нафигачить туда кучу данных и потом несколько горутин их будут разбирать паралельно. И простые, чем то напоминающие вызов функции.

Вообще язык с виду простой, но нюансов там ппц. В целом идея его мне нравится. А вот синтаксис не очень. Хотя вот в том же эрланге мне нехватало структур, а тут из коробки.

anonymous
()
Ответ на: комментарий от anonymous

В целом идея его мне нравится. А вот синтаксис не очень. Хотя вот в том же эрланге мне нехватало структур, а тут из коробки.

Глянь в сторону elixir.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

Да Go тупо быстрее, да и вообще сочетает кучу нужных фич. Типизация, структуры данных, горутины, интересный вариант ооп с учетом более 30 лет выстрелов себе в ногу ранних вариантов. И компиляция конечно же. А еще он будет нативно поддерживаться андроидом. Эрланг норм только не тянет на язык общего назначения. А вот Go вполне может потеснить даже джаву.

anonymous
()
Ответ на: комментарий от Debasher

Я вижу в этом только плюс, когда канал забьется вся работа пойдет на его разгребание, а не полное засирание всей доступной памяти.

anonymous
()

Да, и кстати, эта парадигма нам как-бы, в очередной раз, намекает, что никакой разницы между функциями и данными нет, BTW.

В компутере всё состоит из 0 и 1. Всё что выше это синтаксический сахар.

Любую компутерную логическую парадигму в конце концов реализует машинный код, который сводится к бинарному потоку данных и операций с ним. Всё зависит на каком уровне абстракций ты захочешь остановится.

vtVitus ★★★★★
()
Ответ на: комментарий от vtVitus

Ты меня натолкнул на мысль что надо посоветовать анонимусу пристально поизучать машину тьюринга, её вариации, ну и близкие темы. Конечные автоматы по-моему.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.