I'm trying to "Ramdafy" a function and make it point-free. This is how far I came:
const hasFetchedForFilterQueryString = state => {
const filterQueryString = getFilterQueryString(state);
return either(
pipe(getPages, has(filterQueryString)),
pipe(getNewlyLoadedUsers, has(filterQueryString)),
)(state);
};
// or
const hasFetchedForFilterQueryString = state =>
either(
pipe(getPages, has(getFilterQueryString(state))),
pipe(getNewlyLoadedUsers, has(getFilterQueryString(state))),
)(state);
I wonder if there is a way to write even more point-free, so without holding filterQueryString
in a variable and having to manually apply state
?
I'm usually with Bergi's comment. Point-free should not be a goal on its own. I was enamored of it when I started writing Ramda, and there are a number of functions in it designed to help make things point-free. I rarely recommend them anymore. I don't think that either useWith
or converge
often make functions easier to read or maintain.
But there is a function, lift
, known across the FP world which is helpful here. I think of lift
as lifting a function from the realm of values to the realm of containers of those values. Thus lift (add) (Maybe(3), Maybe(5)) //=> Maybe(8)
. When used with functions that yield our values, it might work like this:
lift ((a, b) => a + b) (
n => n * 2,
n => n - 1
) (5)
//=> (5 * 2) + (5 -1) //=> 14`.
And that is precisely what you want to do with your function. So here is a lift
-based point-free version:
const hfffqs2 = either (
lift (has) (getFilterQueryString, getPages),
lift (has) (getFilterQueryString, getNewlyLoadedUsers)
)
I find this more readable than either of your versions above.
I think that version rivals the simplest non-Ramda version I can come up with for readability:
const hfffqs3 = state => {
const query = getFilterQueryString(state);
return query in getPages(state)
|| query in getNewlyLoadedUsers(state)
}
It's not necessarily more readable, though. And it's less efficient as -- beyond the additional function invocations -- it will have to call getFilterQueryString
twice. But it is a readable and maintainable function.
If lift
is still not quite clear, an intermediate format might look like this:
const hfffqs1 = either (
state => has(getFilterQueryString(state), getPages(state)),
state => has(getFilterQueryString(state), getNewlyLoadedUsers(state))
)
By applying getFilterQueryString
and getState
to lift (has)
we get the equivalent of state => has(getFilterQueryString(state), getPages(state))
, and similarly with the next line. We've lifted the function has
from the realm of strings and objects to that of functions which return strings and objects. We can pass state
to these two functions to get a string and an object.
You can see these in action in the following snippet:
// Dummy implementations just for demo. `state` is a small integer.
const getFilterQueryString = (state) => ['foo', 'bar', 'baz', 'qux', 'corge', 'grault'][state]
const getPages = (state) => [{foo: 1, bar: 2}, {foo: 1, corge: 2, grault: 3}, {baz: 1}, {foo: 1, grault: 2}, {corge: 1, baz: 2}, {bar: 1, qux: 2}][state]
const getNewlyLoadedUsers = (state) => [{baz: 1, qux: 2}, {corge: 1, grault: 2}, {foo: 1, corge: 2}, {baz: 1, qux: 2}, {foo: 1, qux: 2}, {bar: 1, corge: 2}][state]
const hasFetchedForFilterQueryString = state => {
const filterQueryString = getFilterQueryString(state);
return either(
pipe(getPages, has(filterQueryString)),
pipe(getNewlyLoadedUsers, has(filterQueryString)),
)(state);
};
console .log ([0, 1, 2, 3, 4, 5].map(hasFetchedForFilterQueryString))
const hfffqs1 = either (
state => has(getFilterQueryString(state), getPages(state)),
state => has(getFilterQueryString(state), getNewlyLoadedUsers(state))
)
console .log ([0, 1, 2, 3, 4, 5].map(hfffqs1))
const hfffqs2 = either (
lift (has) (getFilterQueryString, getPages),
lift (has) (getFilterQueryString, getNewlyLoadedUsers)
)
console .log ([0, 1, 2, 3, 4, 5].map(hfffqs2))
const hfffqs3 = state => {
const query = getFilterQueryString(state);
return query in getPages(state)
|| query in getNewlyLoadedUsers(state)
}
console .log ([0, 1, 2, 3, 4, 5].map(hfffqs3))
.as-console-wrapper {min-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>
<script> const {either, pipe, has, lift} = R </script>