-
Notifications
You must be signed in to change notification settings - Fork 5
Add a left-to-right composition function (flow
?) and re-work or remove compose
#5
Comments
Note that this is called I think it would be used much more than export const recordParser = Function.flow(
base64Decode,
JSON.parse,
SomeSchema.validate,
ev => sendMessage('hello ' + ev.user.name)
) Reading left-to-right makes it intuitive vs |
Thanks @mmkal. I've added |
To bring it full circle (and hopefully minimize any unclarity before I start my work week), I'll rephrase my suggestion in terms of the the aforementioned "needs" table. I am arguing the following. That, based on my own experience, the need to define a function in terms of a series of function calls in left-to-right order, and maybe even the need to (partially) compose two unary functions in right-to-left order are both greater than the need to define a function in terms of a series of function calls in right-to-left order. Schematically:
The current proposal addresses (And I think that's because this version of One statistic which might support part of this argument is that the |
I appreciate your points. @rbuckton, @tabatkins, @mAAdhaTTah, and I have been talking about whether to include a function-composition helper function and whether to make it flow right-to-left (RTL) or left-to-right (LTR). In mathematics and in many functional-programming languages, the composition operation flows RTL. So some of us had anticipated that FP users would be unhappy if But you have a good point that Lodash’s popular So I am inclined to changing the current RTL As for your curried partial-composition function idea…unfortunately, I am reluctant to include any curried function in this proposal. Programming styles involving currying have been quite controversial in TC39; see tc39/proposal-pipeline-operator#221. I wish to avoid such controversy and increase the probability that this initial proposal be accepted at all. So I’m going to avoid including anything that TC39 would perceive as benefiting or encouraging currying-heavy styles. Sorry if that’s disappointing, and hopefully it’s understandable. |
(Given that built-ins are relatively cheap, especially compared to syntax, I don't think we'll have a particular problem with flow vs pipe competing. The mechanics are similar, but you can't meaningfully use one in place of the other.) |
Some more anecdata to consider. The fp-ts ecosystem uses The PureScript ecosystem, very much derived from Haskell, has both Haskell likewise has I can only speak for myself, but as someone who wants to see FP thrive in JS I'd be happy either way (in stark contrast to my feelings on the pipe operator...). Whatever makes it most likely to get through committee. |
I figured, and understand the reasoning. As I mentioned, I'm happy for that part of my suggestion to be ignored. In this case, I think this issue and #9 are equivalent. |
compose
function into two to better address the problems it solvesflow
?) and re-work or remove compose
I came into it from a bit of an odd angle. I renamed the issue to hopefully aid with discoverability. |
I've noticed that it is not necessarily the case that choosing LTR TransducersThe composition below reads naturally from top-to-bottom (or LTR) and makes sense when compared with its corresponding output ( const calc = arr => arr.reduce(
Function.compose(
onlyIntegers,
double,
addOne
)(append),
[]
)
calc([1, 2, undefined, 3])
// result = [ 3, 5, 7 ] Swapping The rest of the code for this use-case if it's useful: const map = (xform) => (build) => (acc, x) => build(acc, xform(x))
const filter = (pred) => (build) => (acc, x) => pred(x) ? build(acc, x) : acc
const append = (acc, x) => [...acc, x]
const onlyIntegers = filter(Number.isInteger)
const double = map((x) => x * 2)
const addOne = map((x) => x + 1) HTML/VDOMIf you find yourself using const renderBoard = Function.compose(
(tbody) => `
<table class="board">
<tbody>
${tbody.join('')}
</tbody>
</table>
`,
(rows) => rows.map((col) => `<tr>${col.join('')}</tr>`),
(board) =>
board.map((row, py) =>
row.map((col, px) => `
<td
data-action="grid-click"
data-xy="${px},${py}"
>
${cellAppearance[col]}
</td>
`))
) As you can see, Not sure how convincing these are, but hopefully they're enough to show that it's at least possible to have RTL This is not an argument against |
Transducers are funny because—due to their funny wrapping behavior—they seemingly reverse flow direction. Clojure happened to be one of my first FP languages (I truly got into JavaScript via ClojureScript), and, when Rich Hickey introduced transducers, my use of I’m reluctant to include both a RTL and LTR composition function in this initial proposal because several TC39 representatives would probably push back at including both without clear use cases for both. (In fact, I still fear them pushing back at both |
Thanks for the reply and work on this @js-choi , the explanation makes sense. |
The other thing is the transducer protocol is implemented in userland, so unless we were to standardize transducers as a whole, you can't use a typical curried |
Introduction
I think the variadic
compose
function as implemented in Ramda and proposed here occupies a weird space in between two problems, where it doesn't fully solve either. My suggestion is to split the function into two, to address each problem directly.Problem 1: Point-free code linearization / function creation
For example, all statements below are equivalent:
The
Function.pipe
helper allowed the code to be linearized. On top of this, theFunction.compose
helper allowed the point (x
) to be removed. However, the user was asked to reverse the order of their functions for no apparent benefit. Why? Because thiscompose
function carries legacy from solving "problem 2", which we'll get to.From what I can tell (please, someone correct me if I'm wrong), the sole reason for the
compose
function being right-to-left is because the binary function composition combinator does it that way. Doing right-to-left composition of a pair of unary functions makes a lot of sense (I'll expand on that), but doing right-to-left composition in a setting where any number of functions can be given is nothing but impractical.In conclusion, the current
compose
helper is not as ergonomic as it could be for solving the point-free code linearization / function creation problem.Problem 2: Function composition
The curried composition function on two unary functions from combinatory logic, with signature
(b -> c) -> (a -> b) -> a -> c
, can be very useful outside of the context of code linearization. In this scenario, doing the composition right-to-left makes sense because a partially applied composition is a useful tool.In the following example, I use
compose
to define a binary function in terms of another binary function:Here I've partially applied
compose
to make it so it takes one more function to compose. Then when the outer compose is supplied its argument, the partially appliedadd
function is baked into the composition withMath.sqrt
. This is just one example of how partial application ofcompose
can serve some kind of purpose. This is the space where the curried unary compose function shines.Using the variadic
compose
function for this was uncomfortable, because, while its argument order (right-to-left) suggests that this is the purpose it serves, I had to use.bind
to partially apply it.In conclusion, the current
compose
helper is not as ergonomic as it could be for solving the function composition problem.Solution: Addressing these two problems separately
I'm proposing addressing each of the problems that are now partially addressed by the variadic
compose
function as two separate problems. These problems can both be addressed more directly to provide a better developer experience.Instead of the variadic
compose
with reversed function order, I would propose solving this problem more directly with a composition function that applies functions left to right. Infp-ts
this function was dubbedflow
. But in Ramda and Sanctuary, this function is the one calledpipe
.The code example under Problem 1 would now be:
This is both easier to read, and easier to refactor from and to
x => Function.pipe(x, Math.sin, Math.round, JSON.stringify)
Instead of the variadic
compose
function, this problem is best addressed by a unary curried function composition combinator, exactly as implemented (namedB
) in my Gist.The code example under Problem 2 would now be:
In my ideal world, both of these functions would make it into this proposal. But I feel less strongly about including the B combinator, than I feel about fixing (the argument order in)
compose
to better address problem 1. I'd also expect more pushback against theB
combinator.The text was updated successfully, but these errors were encountered: