I have a data structure I call a 'spec' which looks like this:
const spec = {
command: {
name: 'name',
description: 'description',
alias: 'alias',
arguments: '_children/Arguments'
},
arguments: {
name: 'name',
alias: 'alias',
optional: 'optional',
description: 'description'
}
};
So the elements inside of command and arguments are properties mapped to paths. The best illustration of this is spec.command.arguments. What I need to do is translate this into another object with the same shape, but the paths are converted into Ramda lenses (using R.lensPath).
So conceptually, this is translated into something like this:
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')
}
};
The above is not meant to be taken literally, it is a pseudo structure. For example lens('_children/Arguments') just represents a lens built using Ramda lensPath.
So here is my code:
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);
The key point of failure I think is inside the reducer function, which returns the updated accumulator (acc[field] = lens;). For some reason which I can't understand, this assignment is being lost, and the accumulator is not being correctly populated on each iteration. As you can see from the code sample, the initial value passed into reduce is an object with a single dummy property. The result of the reduce is incorrectly just this single dummy value and not all the fields with their respective Ramda lenses.
However, what's really gonna bake your noodle is that the exact same code running in Ramda repl exhibits different behaviour, see this code in the repl at: Ramda code
I'm running node version 10.13.0
The result that the Repl code produces is this:
{
'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)))
}
}
}
}
As you can see, the result looks a bit complicated because the values of each property is the lens created by lensProp.
This is in contrast to the following (note that order of command and arguments is reversed, but this shouldn't be significant):
{
'command': {
'dummy': '***'
},
'arguments': {
'dummy': '***'
}
}
which is being returned in my unit test.
I've wasted about 2 days on this and have now admitted defeat, so hopefully, somebody can shed some light on this. Cheers.
JSON.stringifywon't give you useful results on function properties.) You return an object withargumentsandcommandproperties, each of which is an object containingname,aliasargumentsandoptionalproperties, each holding a lens. Is that not what you want?dummyproperty, you can writelensifymuch more simply asconst lensify = map(pipe(split('/'), lensPath)).JSON.stringify. It uses the REPL's more sophisticated display feature.JSON.stringify(makeLenses(spec)) //=> "{\"command\":{\"dummy\":\"***\"},\"arguments\":{\"dummy\":\"***\"}}"lensifyaltogether withconst makeLenses = map(map(pipe(split('/'), lensPath))), assuming that you want to apply it to all elements of your spec.