Как исправить неправильный результат возврата из функции уменьшения Ramda (JavaScript) - PullRequest
1 голос
/ 31 мая 2019

У меня есть структура данных, которую я называю «spec», которая выглядит так:

const spec = {
  command: {
    name: 'name',
    description: 'description',
    alias: 'alias',
    arguments: '_children/Arguments'
  },
  arguments: {
    name: 'name',
    alias: 'alias',
    optional: 'optional',
    description: 'description'
  }
};

Таким образом, элементы внутри command и arguments являются свойствами, сопоставленными с путями. Лучшая иллюстрация этого - spec.command.arguments . Что мне нужно сделать, это перевести это в другой объект с той же формой, но пути преобразуются в линзы Ramda (используя R.lensPath).

Итак, концептуально это переводится примерно так:

const spec = {
  command: {
    name: lens('name'),
    description: lens('description'),
    alias: lens('alias'),
    arguments: lens('_children/Arguments')
  },
  arguments: {
    name: lens('name'),
    alias: lens('alias'),
    optional: lens('optional'),
    description: lens('description')
  }
};

Вышесказанное не следует понимать буквально, это псевдоструктура. Например, lens ('_ children / Arguments') просто представляет объектив, созданный с использованием Ramda lensPath.

Так вот мой код:

const spec = {
  command: {
    name: 'name',
    description: 'description',
    alias: 'alias',
    arguments: '_children/Arguments'
  },
  arguments: {
    name: 'name',
    alias: 'alias',
    optional: 'optional',
    description: 'description'
  }
};

function lensify (spec) {
  const result = R.pipe(
    R.toPairs,
    R.reduce((acc, pair) => {
      const field = pair[0];
      const path = pair[1];
      const lens = R.compose(
        R.lensPath,
        R.split('/')
      )(path);

      acc[field] = lens; // Is there something wrong with this, if so what?
      return acc;
    }, { dummy: '***' }) // list of pairs passed as last param here
  )(spec);

  // The following log should show entries for 'name', 'description', 'alias' ...
  console.log(`+++ lensify RESULT: ${JSON.stringify(result)}`);
  return result;
}

function makeLenses (spec) {
  const result = {
    command: lensify(spec.command),
    arguments: lensify(spec.arguments)
  };

  return result;
}

makeLenses(spec);

Ключевая точка отказа, я думаю, находится внутри функции редуктора, которая возвращает обновленный аккумулятор ( acc [field] = lens; ). По некоторым причинам, которые я не могу понять, это назначение теряется, и аккумулятор не заполняется правильно на каждой итерации. Как видно из примера кода, начальное значение, передаваемое в lower, является объектом с одним свойством dummy . Результатом уменьшения является неверно только это единственное фиктивное значение, а не все поля с соответствующими линзами Ramda.

Однако, что действительно испечет вашу лапшу, так это то, что один и тот же код, запущенный в repda Ramda, демонстрирует другое поведение, этот код можно увидеть в ответе по адресу: Код Ramda

Я использую узел версии 10.13.0

Результат, который дает код Repl, таков:

{
  'arguments': {
    'alias': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    },
    'description': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    },
    'dummy': '***',
    'name': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    },
    'optional': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    }
  },
  'command': {
    'alias': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    },
    'arguments': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    },
    'description': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    },
    'dummy': '***',
    'name': function (r) {
      return function (e) {
        return z(function (t) {
          return n(t, e)
        }, r(t(e)))
      }
    }
  }
}

Как видите, результат выглядит несколько сложным, поскольку значения каждого свойства - это объектив, созданный lensProp.

Это в отличие от следующего (обратите внимание, что порядок команд и аргументов поменялся местами, но это не должно быть значительным):

{
  'command': {
    'dummy': '***'
  },
  'arguments': {
    'dummy': '***'
  }
}

, который возвращается в моем модульном тесте.

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

Ответы [ 2 ]

1 голос
/ 31 мая 2019

Это показывает простейшее использование вашего вывода, которое я могу себе представить, сопоставляя view на линзах с общим объектом.Кажется, он работает правильно как в REPL, так и во фрагменте, и в Node 10.13.0:

const {map, pipe, split, lensPath, view} = ramda  

const makeLenses = map ( map ( pipe ( split ('/'), lensPath )))

const applyLensSpec = (lensSpec) => (obj) => 
  map ( map ( f => view (f, obj) ), lensSpec)

const spec = {command: {name: "name", description: "description", alias: "alias", arguments: "_children/Arguments"}, arguments: {name: "name", alias: "alias", optional: "optional", description: "description"}};

const myTransform = applyLensSpec(
  makeLenses(spec),
)

const testObj =   {
  name: 'foo', 
  alias: 'bar', 
  description: 'baz', 
  optional: false, 
  _children: {
    Arguments: ['qux', 'corge']
  }
}

console .log (
  myTransform (testObj)
)
<script src="https://bundle.run/ramda@0.26.1"></script>
0 голосов
/ 02 июня 2019

В добавлении к этому сообщению и в соответствии с тем, что сказал Скотт, причиной этого сообщения является недостаток JSON.stringify, и это фактически является моралью этой истории;не всегда доверяйте выводу JSON.stringify.Вот тестовый пример, который подтверждает это:

  context('JSON.stringify', () => {
    it.only('spec/lensSpec', () => {
      const spec = {
        command: {
          name: 'name',
          description: 'description',
          alias: 'alias',
          arguments: '_children/Arguments'
        },
        arguments: {
          name: 'name',
          alias: 'alias',
          optional: 'optional',
          description: 'description'
        }
      };

      const makeLensSpec = R.map(R.map(R.pipe(
        R.split('/'),
        R.lensPath
      )));

      const lensSpec = makeLensSpec(spec);
      console.log(`INPUT spec: ${JSON.stringify(spec)}`);
      // The following stringify does not truly reflect the real value of lensSpec.
      // So do not trust the output of JSON.stringify when the value of a property
      // is a function as in this case where they are the result of Ramda.lensProp.
      //
      console.log(`RESULT lensSpec: ${JSON.stringify(lensSpec)}`);
      const rename = {
        'name': 'rename',
        'alias': 'rn',
        'source': 'filesystem-source',
        '_': 'Command',
        'describe': 'Rename albums according to arguments specified.',
        '_children': {
          'Arguments': {
            'with': {
              'name': 'with',
              '_': 'Argument',
              'alias': 'w',
              'optional': 'true',
              'describe': 'replace with'
            },
            'put': {
              'name': 'put',
              '_': 'Argument',
              'alias': 'pu',
              'optional': 'true',
              'describe': 'update existing'
            }
          }
        }
      };

      // NB, if the output of JSON.stringify was indeed correct, then this following
      // line would not work; ie accessing lensSpec.command would result in undefined,
      // but this is not the case; the lensSpec can be used to correctly retrieve the
      // command name.
      //
      const name = R.view(lensSpec.command.name, rename);
      console.log(`COMMAND name: ${name}`);
    });
  });

Примечания к журналу:

console.log (INPUT spec: ${JSON.stringify(spec)});

, который отображает это:

INPUT spec: {"command":{"name":"name","description":"description","alias":"alias","arguments":"_children/Arguments"},"arguments":{"name":"name","alias":"alias","optional":"optional","description":"description"}}

console.log (RESULT lensSpec: ${JSON.stringify(lensSpec)});

Это ошибка (lenSpec содержит свойства, значения которых являются функциямикоторый stringify не может отображать, поэтому пропускает их полностью, давая неправильное представление:

RESULT lensSpec: {"command":{},"arguments":{}}

console.log (COMMAND name: ${name});

Это работаеткак и ожидалось:

COMMAND name: rename

Примечание: я только что нашел это: Почему JSON.stringify не отображает свойства объекта, которые являются функциями?

...