понедельник, 24 января 2022 г.

[prog] Деформация на почве иммутабельности

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

При программировании на C++ это выражается в том, что стараюсь по максимуму все объявлять const-ом и делать мелкие вспомогательные функции, которые не содержат внутри себя изменяемых переменных (это нужно для того, чтобы уменьшить количество переменных там, где эти мелкие функции затем вызываются).

Иногда стремление к иммутабельности приобретает странные формы.

Вот сегодня, например, попал на ревью такой код (схематично, к реальности имеет отношение только структура):

void do_something(const first_argument & a, const second_argument & b)
{
   int v_one = some_calculation(a, b);
   int v_two = another_calculation(a, b);

   some_type x = some_type(const_1, const_2);
   some_type y = some_type(const_3, const_4);

   if(v_one < v_two) {
      int delta = detect_delta(a, b, v_one);
      x = some_type(const_5 + delta, const_6);
      y = some_type(const_7, const_8 + delta);
   }
   else if(v_one > v_two) {
      int delta = detect_delta(a, b, v_two);
      x = some_type(const_5, const_6 + delta);
      y = some_type(const_7 + delta, const_8);
   }

   call_external_lib_function(a, b, x, y);
}

И у меня руки так и чесались заменить его вот на такой:

void do_something(const first_argument & a, const second_argument & b)
{
   struct result {
      some_type x, y;
   };
   
   const auto r = [&]() -> result {
         const int v_one = some_calculation(a, b);
         const int v_two = another_calculation(a, b);

         if(v_one < v_two) {
            const int delta = detect_delta(a, b, v_one);
            return {
               some_type(const_5 + delta, const_6),
               some_type(const_7, const_8 + delta)
            };
         }
         else if(v_one > v_two) {
            const int delta = detect_delta(a, b, v_two);
            return {
               some_type(const_5, const_6 + delta),
               some_type(const_7 + delta, const_8)
            };
         }
         else {
            return {
               some_type(const_1, const_2),
               some_type(const_3, const_4)
            };
         }
      }();

   call_external_lib_function(a, b, r.x, r.y);
}

Но удержался.

Т.к. не уверен, что найду понимание в своем радикализме.

Поэтому ограничился минимумом: просто все переменные, которые получают значения один раз, пометил как const.

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

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

Комментариев нет: