-
Notifications
You must be signed in to change notification settings - Fork 633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(functions): adding a functions module with pipe() #6143
base: main
Are you sure you want to change the base?
Conversation
Are I personally think inline arrow functions express the operation like const myNewFunc = set_arguments(myFunc, 1, undefined, 3);
// vs
const myNewFunc = (x: number) => myFunc(1, x, 3); |
Both
|
I don't understand this. Inline arrow function example should work with any parameters available in that scope. I think that has the same capability as what BTW do you suggest
Can you elaborate on more specific examples where const func = pipe(Math.abs, Math.sqrt, Math.floor);
//
const func = (num: number) => Math.floor(Math.sqrt(Math.abs(num))) |
I see Yes, of course, you can create an arrow function to inject parameters: const fnWithFirstDeps = (dataArg: Parameters<typeof fn>[2]) => fn(dependency1, dependency2, dataArg) I think it's a common and rather generic need, so having a utility that obviates the need to create a local function for it and improves readability is handy, while also encouraging a good coding practice (dependency injection). Sometimes different parts of the code inject different dependencies, having a utility encourages doing the injection locally and not creating another function or re-using the above function which entangles concerns. const fnWithFirstDeps = setArguments(fn, dependency1, dependency2,)
const extracted = {
bedrooms: pipe(
selectOne.attributes,
parseMatchingText("bedrooms"),
removeLabel,
parseNumber,
),
bathrooms: pipe(
selectOne.attributes,
h.parseMatchingText("bathrooms"),
removeLabel,
parseNumber,
),
} |
I'm also not a fan of these typings. |
(BTW while I'm personally not in favor of this, we are open for adding this package if there's enough community support to this idea.) |
Typing for a number of arguments is very common and gives a good experience. As far as I can see, it's a theoretical, not a practical issue. |
This PR and #4386, which has been open since February, have had near-zero community support. Perhaps, it'd be best to have this done as a 3rd party package and see if it evolves enough to provide a stronger argument for its addition sometime in the future. |
Honestly |
@guy-borderless Can you limit this PR to |
Of course! On it. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #6143 +/- ##
==========================================
- Coverage 96.43% 96.37% -0.06%
==========================================
Files 547 552 +5
Lines 41812 41895 +83
Branches 6347 6350 +3
==========================================
+ Hits 40321 40377 +56
- Misses 1450 1478 +28
+ Partials 41 40 -1 ☔ View full report in Codecov by Sentry. |
c7df99b
to
3a08d69
Compare
Per feedback, PR was limited to pipe() and tests were added. |
You also got to get all the tests to pass. Looks like the title isn't named right and you're missing the copyright notice in some of the files. |
The PR naming issue seems to be because the functions module is new and so a scope doesn't exist for it. Added the copyright notice, thanks for mentioning it (didn't seem to cause a test to fail) |
bd18d57
to
9fc723a
Compare
I believe a change needs to happen in |
should I add the functions module to import_map.json here? |
Yes |
bc0f426
to
df3f242
Compare
d54ec2e
to
e03206b
Compare
cc @andrewthauer Do you find this |
I would note that the |
* @param input The functions to be composed | ||
* @returns A function composed of the input functions, from left to right | ||
*/ | ||
export function pipe(): <T>(arg: T) => T; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this first overload? What is this for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is my habit that if an edge case has a natural interpretation I include it by default, even without a concrete use-case. This isn't a strong opinion, happy to remove this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would note that a scenario for this will, of course, involve .apply()
. Again, I don't mind.
Sorry just saw this. I'll pull the PR down and play around to see how well it works in regards to addressing #4386 |
First, I will say, that the behaviour of pipe functions varies across libraries for what they call Some libraries auto curry based on the data or function args or do not curry at all. Others distinguish the difference with something like a Ok, so here are my initial thoughts on the Pipeline typing works pretty well through pipeline const myPipe = pipe(
(num: number) => String(num),
(str) => Number(str),
);
myPipe(3)
^^^ accepts number returns string! Pipeline function arguments are not inferred This is where typing could be improved ... const myPipe = pipe(
(num: number) => String(num),
(str) => Number(str),
^^^ Ideally this would be inferred as a string
); Does not support for multiple arguments in first function Not sure this is a show stopper, but it would be nice to support multiple arguments in the first function. The const myPipe = pipe(
(a: number, b: number) => a + b,
(str) => Number(str),
);
myPipe2(3, 2);
^^^^ expected 1 args but provided 2 In general this is a good start with initial testing, but could have improved typing imo ( |
Just mentioning that function arguments should be inferred in this
implementation. Checking when near a computer
…On Mon, Jan 20, 2025, 07:56 Andrew Thauer ***@***.***> wrote:
First, I will say, that the behaviour of pipe functions varies across
libraries for what they call pipe. So first off I'd like to loosely
classify this version as a data-last curried pipe function that works
left to right.
Some libraries auto curry based on the data or function args or do not
curry at all. Others distinguish the difference with something like a flow
function (e.g. fs-ts's flow
<https://gcanti.github.io/fp-ts/modules/function.ts.html#flow>) vs pipe
<https://gcanti.github.io/fp-ts/modules/function.ts.html#pipe>.
Ok, so here are my initial thoughts on the pipe function in it's current
state:
*Pipeline typing works pretty well through pipeline*
const myPipe = pipe(
(num: number) => String(num),
(str) => Number(str),);
myPipe(3)^^^ accepts number returns string!
*Pipeline function arguments are not inferred*
This is where typing could be improved ...
const myPipe = pipe(
(num: number) => String(num),
(str) => Number(str),
^^^ Ideally this would be inferred as a string);
*Does not support for multiple arguments in first function*
Not sure this is a show stopper, but it would be nice to support multiple
arguments in the first function. The fs-ts flow
<https://gcanti.github.io/fp-ts/modules/function.ts.html#flow> function
supports this which is nice.
const myPipe = pipe(
(a: number, b: number) => a + b,
(str) => Number(str),);
myPipe2(3, 2);
^^^^ expected 1 args but provided 2
In general this is a good start with initial testing, but could have
improved typing imo (fs-ts does a good job here), If I need a quick pipe
and didn't already have another functional library pulled in I might make
use of it. That said, without a builtin curry/purry function it would
likely be a bit verbose to define a pipeline of functions that have
multiple arguments (non unary arity). Although this is perhaps not a fault
of the pipe function itself. However, the lack of a curry function makes it
less unseful from a functional perspective imo.
—
Reply to this email directly, view it on GitHub
<#6143 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AX35W655K5SWMCER4ZVKDTD2LRCVLAVCNFSM6AAAAABQQN6GY6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMMBRGEYTSNZQG4>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Andrew, thanks for your feedback. Initially, I considered requiring all pipe functions to be unary, even though it’s unnecessary for the first one, to simplify reordering functions within the pipe if the need arise. upon reflection and given deno-std’s unopinionated approach, this restriction does feel unnecessary, so I now removed it. I’ve added argument typings for up to 7 pipe functions and can extend it further if needed. While I find value in using this implementation of pipe without currying, I agree that including a curry function would be very useful. It would naturally complement pipe and enhance flexibility in the future. |
Hi,
I'd like to suggest adding an std module for utilities related to functions.
related: #4386
There are common manipulations to functions implemented in numerous npm packages and toolbelts, and using them where appropriate greatly improves code quality. It would be handy to have these in deno_std.
I want to suggest for starters the following two:
These two scenarios have some typescript knowledge behind them and it makes sense to reach for a proper utility.
Both of these have many different implementations in terms of code and typings. In my opinion, the implementation most in line with the pragmatic spirit of the std should avoid functional programming jargon for simplicity and accessibility to beginners.
For discussion in this PR I have included two implementations (without tests) that I think would suit the deno_std
pipe has good typescript performance and small implementation. I'm also using it in several projects.
set_arguments has a very non-academic intuitive signature that I think suites nicely with the deno_std. This signature does not expect a data-last coding convention, so it doesn't leak to the rest of the code. I have also provided a more classic functional programming implementation of this called curry to contrast.
I can significantly simplify the typing definition of
set_arguments
if you'd like me to go ahead with preparing this PR.Since functions are an important primitive in JS, I'm sure more utilities will be added.