I have some data in the form:
const data = {
list: [1, 2, 3],
newItem: 5
}
I want to make a function that appends the value of newItem
to list
resulting in this new version of data
:
{
list: [1,2,3,5],
newItem: 5,
}
(Ultimately, I'd remove newItem after it's been moved into the list, but I'm trying to simplify the problem for this question).
I'm trying to do it using pointfree style and Ramda.js, as a learning experience.
Here's where I am so far:
const addItem = R.assoc('list',
R.pipe(
R.prop('list'),
R.append(R.prop('newItem'))
)
)
The idea is to generate a function that accepts data
, but in this example the call to R.append
also needs a reference to data, I'm trying to avoid explicitly mentioning data
in order to maintain Pointfree style.
Is this possible to do without mentioning data
?
const addItem = R.chain
( R.assoc('list') )
( R.converge(R.append, [R.prop('newItem'), R.prop('list')]) );
const data = {
list: [1, 2, 3],
newItem: 5
};
console.log(addItem(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
And here is why:
First we can have a look at what the current addItem
is supposed to look like when not point free:
const addItem = x => R.assoc('list')
(
R.pipe(
R.prop('list'),
R.append(R.prop('newItem')(x))
)(x)
)(x);
console.log(addItem({ list: [1, 2, 3], newItem: 5 }));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
It takes some data and uses it in three places. We can refactor a bit:
const f = R.assoc('list');
const g = x => R.pipe(
R.prop('list'),
R.append(R.prop('newItem')(x))
)(x)
const addItem = x => f(g(x))(x);
console.log(addItem({ list: [1, 2, 3], newItem: 5 }));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
The x => f(g(x))(x)
part might not be obvious immediately but looking at the list of common combinators in JavaScript it can be identified as S_:
Name | # | Haskell | Ramda | Sanctuary | Signature |
---|---|---|---|---|---|
chain | S_³ | (=<<)² | chain² | chain² | (a → b → c) → (b → a) → b → c |
Thus x => f(g(x))(x)
can be simplified pointfree to R.chain(f)(g)
.
This leaves the g
which still takes one argument and uses it in two places. The ultimate goal is to extract two properties from an object and pass them to R.append()
, this can be more easily (and pointfree) be expressed with R.converge()
as:
const g = R.converge(R.append, [R.prop('newItem'), R.prop('list')]);
Substituting the f
and g
back gives
const addItem = R.chain
( R.assoc('list') )
( R.converge(R.append, [R.prop('newItem'), R.prop('list')]) );