Hacker News new | ask | show | jobs
by btown 1412 days ago
This is quite elegant!

Direct link to the source of OP's pipe function and recursive type definitions: https://github.com/MathisBullinger/froebel/blob/main/pipe.ts...

On a related note, I've been frustrated by the slow progress on https://github.com/tc39/proposal-pipeline-operator - specifically the thread in https://github.com/tc39/proposal-pipeline-operator/issues/91 which has 668 comments over 4+ years and shows no meaningful sign of consensus.

The TC39 group is (justifiably) very concerned about backwards and future compatibility, and Typescript has a policy of not introducing syntax that is in scope for Javascript itself until the syntax has formally reached a stable state (see: https://github.com/Microsoft/TypeScript/issues/2103#issuecom...) - so we're far from having a pure operator for this.

But `pipe((n: number) => n.toString(), (a: string) => a+' ')(3)` is as clean as I've ever seen it get.

And to use it in an ad-hoc way for left-to-right readability (and for code that will be maintained by those who think curry is just a tasty dish), it's trivial to implement an applyPipe on top:

    applyPipe('foo', strip, title, (s: string) => `${s}: bar`)
OP - it would be great to have that, or something named slightly better, out of the box!
5 comments

Thanks for linking the code, I meant to link to the pipe section of the README but obviously botched that and can't change it now :)

I was really exited about the pipe proposal and actively followed the discussion like 4 years ago. But with every passing year of no progress my excitement is slowly dying.

An `applyPipe` function is an interesting idea. My only concern with it is that the first function in the pipe could have more (or less) than one argument. And in that case how would you know where the arguments stop and the functions begin? Maybe the first parameter should be an array of the arguments. Or applyPipe could be of the form applyPipe(1, 2)(add, square, whatever). What do you think? If there is interest in this I'll add it.

If I'm reaching for this form, I'm likely thinking about a pipeline of unary functions, and the TC39 proposal seems to take the same approach. Plus, doesn't every other function in the pipeline need to be unary after the first?

As a half-baked thought, perhaps callPipe would take exactly one non-function argument to start the pipeline (which would appeal to most use cases), and applyPipe could take an array of args, mimicking the call/apply duality in native JS?

Yes, every function except the first one needs to have exactly one argument.

Others have also said that `pipe` is not an ideal name for the function. So perhaps renaming it to `compose` and have `pipe` be of the form `pipe(arg, funA, funB, ...)` (like you suggested for `applyPipe`) might be the solution. Will need to think about it a bit more.

Note ‘compose’ conventionally connotes a right-to-left order, so that (f ∘ g ∘ h)(x) = f(g(h(x))).
Is there an implementation where ‘then’ is added to Object.prototype as:

  function(f, ...preargs) { return f(...preargs, this) }
And then you can use it to pipeline sync and async things together? It’s still kinda bad and seems pretty crazy though. Some obvious issues are:

- the preargs thing presumably doesn’t work for promises so you’d need to use bind.

- there’s no great way to specify ‘the function which is property foo of the object being operated on’ to e.g. map an array that is in a promise you can’t write .then(.map, …). But I think this is also a problem with other pipe implementations.

I think `Object.prototype.then` would break every source file that uses `typeof o?.then === 'function'` as a check for PromiseLike behavior.
Trying to understand this: is the lambda symbol defined somewhere globally? How can they just use it Willy nilly as a generic Stand-in for a spread parameter type?
It's just an identifier like any other defined in types.ts: https://github.com/MathisBullinger/froebel/blob/main/types.t...

It's defined as

  type λ<TA extends any[] = any[], TR = any> = (...args: TA) => TR;
meaning λ is just any function and λ<[number], string> would be the type of a function that takes a number as its only argument and returns a string.
Ok that’s what I thought, thanks!
JS (and TS thereby) allows arbitrary unicode in identifiers (variable names, class names, [TS] type names). The lambda symbol is just acting as the name of a type parameter instead of something more ASCII usual like `T` or `F`.

There's a lot of interesting discussions around it as some math libraries like to use math symbols and greek letters and sometimes even emoji as variable/class names in JS and other developers have a gut reaction that they do not like it.

in the Python library `returns`, `applyPipe` is called `flow`, and I agree that it's a very handy utility to have on top of a pipe.
applyPipe is added now: https://github.com/MathisBullinger/froebel#applypipe

It's included in version 0.21.0