Skip to content
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

Support proposed ES Next "|>" pipeline operator #17718

Open
graingert opened this issue Aug 10, 2017 · 79 comments

Comments

@graingert
Copy link

commented Aug 10, 2017

No description provided.

@AlexGalays

This comment has been minimized.

Copy link

commented Aug 10, 2017

My favourite proposal ever :( Nowadays, we can really write this free programs.

@aluanhaddad

This comment has been minimized.

Copy link
Contributor

commented Aug 10, 2017

For reference, the TC39 proposal: https://github.com/tc39/proposal-pipeline-operator

@Pajn

This comment has been minimized.

Copy link

commented Aug 15, 2017

Not that the proposal is not even at stage 0 yet. If it ever is added to the language semantics and other details will likely change.

@AlexGalays

This comment has been minimized.

Copy link

commented Aug 15, 2017

This would be a first I think (beside some oldies like Enum and the module system) but could typescript implementing this give it more visibility and boost demand for it in the rest of the ecma ecosystem?

@PublicParadise

This comment has been minimized.

Copy link

commented Aug 15, 2017

Just wanted to share a workaround for the missing pipeline operator inspired by https://vanslaars.io/post/create-pipe-function/...

SyncPipe with synchronous reduction

// SyncPipe with synchronous reduction
type SyncPipeMapper<T, U> = (data: T | U) => U;
type SyncPipeReducer<T, U> = (f: SyncPipeMapper<T, U>, g: SyncPipeMapper<T, U>) => SyncPipeMapper<T, U>;
type SyncPipe<T, U> = (...fns: SyncPipeMapper<T, U>[]) => SyncPipeMapper<T, U>;
function createSyncPipe<T, U>(): SyncPipe<T, U> {
    const syncPipe: SyncPipeReducer<T, U> = (f: SyncPipeMapper<T, U>, g: SyncPipeMapper<T, U>) => (data: T) => g(f(data));
    return (...fns: SyncPipeMapper<T, U>[]): SyncPipeMapper<T, U> => fns.reduce(syncPipe);
}

// Example:
function testSyncPipe(num: number): number {
    const addOne: SyncPipeMapper<number, number> = (data: number): number => {
        return data + 1;
    }
    const syncPipe: SyncPipe<number, number> = createSyncPipe();
    const syncWaterfall: SyncPipeMapper<number, number> = syncPipe(
        addOne,
        addOne,
        addOne,
    );

    // Does the equivalent of num+3
    const lastnumber: number = syncWaterfall(num);
    return lastnumber;
}

AsyncPipe with asynchronous reduction

// AsyncPipe with asynchronous reduction
type AsyncPipeMapper<T, U> = (data: T | U) => Promise<U>;
type AsyncPipeReducer<T, U> = (f: AsyncPipeMapper<T, U>, g: AsyncPipeMapper<T, U>) => AsyncPipeMapper<T, U>;
type AsyncPipe<T, U> = (...fns: AsyncPipeMapper<T, U>[]) => AsyncPipeMapper<T, U>;
function createAsyncPipe<T, U>(): AsyncPipe<T, U> {
    const asyncPipe: AsyncPipeReducer<T, U> = (f: AsyncPipeMapper<T, U>, g: AsyncPipeMapper<T, U>) => async (data: T) => g(await f(data));
    return (...fns: AsyncPipeMapper<T, U>[]): AsyncPipeMapper<T, U> => fns.reduce(asyncPipe);
}

// Example:
async function testAsyncPipe(num: number): Promise<number> {
    const addOne: AsyncPipeMapper<number, number> = async (data: number): Promise<number> => {
        return data + 1;
    }
    const asyncPipe: AsyncPipe<number, number> = createAsyncPipe();
    const asyncWaterfall: AsyncPipeMapper<number, number> = asyncPipe(
        addOne,
        addOne,
        addOne,
    );

    // Does the equivalent of num+3
    const lastnumber: number = await asyncWaterfall(num);
    return lastnumber;
}

Pipe with asynchronous reduction (simplified)

I use this one most of the time:

// Pipes with asynchronous reduction
type PipeMapper<T> = (data: T) => Promise<T>;
type PipeReducer<T> = (f: PipeMapper<T>, g: PipeMapper<T>) => PipeMapper<T>;
type Pipe<T> = (...fns: PipeMapper<T>[]) => PipeMapper<T>;
function createPipe<T>(): Pipe<T> {
    const pipePipe: PipeReducer<T> = (f: PipeMapper<T>, g: PipeMapper<T>) => async (data: T) => g(await f(data));
    return (...fns: PipeMapper<T>[]): PipeMapper<T> => fns.reduce(pipePipe);
}

// Example:
async function testPipe(num: number): Promise<number> {
    const addOne: PipeMapper<number> = async (data: number): Promise<number> => {
        return data + 1;
    }
    const pipe: Pipe<number> = createPipe();
    const waterfall: PipeMapper<number> = pipe(
        addOne,
        addOne,
        addOne,
    );
    // Does the equivalent of num+3
    const lastnumber: number = await waterfall(num);
    return lastnumber;
}

I hope you will find this helpful!

@AlexGalays

This comment has been minimized.

Copy link

commented Aug 16, 2017

@PublicParadise way too much boilerplate :p

@aluanhaddad

This comment has been minimized.

Copy link
Contributor

commented Aug 16, 2017

While I would definitely like to see some variant of this operator in the language the perceived need for it comes from two different limitations of ECMAScript as it currently stands.

The first is very hard to work around or even address in the language: the inability to extend built in objects in a hygienic manner.

The second however does not need language level support at all and could in fact be rectified: the standard library can kindly be called anemic.

Maximally Minimal is a complete failure.

Why does it take months and months of argument to get Array.prototype.flatMap in the language?

That's one method and it should have been there from the beginning and it should be obvious that it should be added.

Maybe Array.prototype will have a groupBy method in 6 years.

@tycho01

This comment has been minimized.

Copy link
Contributor

commented Sep 23, 2017

By now this has a few babel implementations, which will hopefully help along the TC39 proposal:

@ddimaria

This comment has been minimized.

Copy link

commented Sep 26, 2017

It's in stage 1 now

@galkowskit

This comment has been minimized.

Copy link

commented Sep 29, 2017

So, any chances for this beauty getting into TS? It would be in-line with F#. <3

@kitsonk

This comment has been minimized.

Copy link
Contributor

commented Sep 29, 2017

While there are exceptions, when a proposal is important to TypeScript and types, proposal are not typically implemented until they reach TC39 Stage 3 in TypeScript, as they are not stable enough to ensure that there won't be significant breakage and regressions.

While none of the core team have commented on this issue yet, it wouldn't be in my opinion important enough to be considered for implementation before Stage 3. The best focus is to support the champion and proposal at TC39.

@garkin

This comment has been minimized.

Copy link

commented Oct 26, 2017

If only TS had an option to just pipe this operator through to allow a babel with plugins to deal with it.
Or had own syntax plugins, like post-css do. Several years waiting for a primitive operator is just too much.

@tycho01

This comment has been minimized.

Copy link
Contributor

commented Oct 26, 2017

@garkin: The challenge here is TS needs to understand the code to do its job of providing type safety, which doesn't combine well with random code it doesn't understand. Unless it were to get macros (#4892), in which case it'd just compile to code it does understand. But I wouldn't expect that on the roadmap quite yet, as quite a few bits of the standard library are still challenging to type atm.

@graingert

This comment has been minimized.

Copy link
Author

commented Oct 26, 2017

@AlexGalays

This comment has been minimized.

Copy link

commented Oct 26, 2017

Now that Babel understands typescript you could run it through Babel then
typescript

Twice the build time though :p

@graingert

This comment has been minimized.

Copy link
Author

commented Oct 26, 2017

@garkin

This comment has been minimized.

Copy link

commented Oct 27, 2017

@graingert: It's a nice option to have, i'll investigate this.
Unfortunately, it won't work with typescript Language Service API which is used by VisualStudioCode, Webstorm and other IDEs.

@kbtzr

This comment has been minimized.

Copy link

commented Oct 27, 2017

Regarding "TS plugins" one could easily achieve the desired result with, let's say, a simple (pre)transpiler for the pipe operator that understands TS syntax and produces it's strong typed equivalent of the statement. It would compile just fine with type checking and whatnot.

A webpack configuration for that could look something like this:

module: {
		rules: [
			{ test: /\.ts$/, loader: 'ts-pipe-operator', enforce: 'pre' },
			{ test: /\.ts$/, loader: 'ts-loader' },
			...
        ]
 }

The only challenge, as pointed by @garkin, is that TS service wouldn't be able to correlate transpiled parts to the original source file, then IDEs that uses the service wouldn't work properly even if they already recognize the operator (ES Next syntax enabled or something).

Perhaps if we create a NFR (or maybe there's already one?) for TS service to support cumulative source maps to be applied between the source file and the transpiled result that is feed to the compiler, this and other plugins would be possible without affecting the rest of the community and, most importantly, without adding more complexity for the core team to deal with.


Also, I can't tell how much #13940 is related to this but its apparently a good start towards more complex plugins. However it seems to me that the source map approach still much simpler alternative since a minimalistic (pre)transpiler wouldn't need the project context for most cases as it would be fairly easy to just extract the type notation blocks (if any) from the statement raw text and then rewrite it in a way that control flow will be able to imply the specific I/O types for the transpiled part.


Last but not least, could anyone please point me out to the official thread (if any) regarding this kind of plugins?

@garkin

This comment has been minimized.

Copy link

commented Oct 27, 2017

I need to say i realy appreciate calm way of introducing new proposals and prefer the monolithic Typescript and LessCSS tooling much more, than Flow+Babel and Post-CSS special plugins olympics.

It's a customizabilty and a speed of getting new features in cost of bloating fragmentation and an area of expertise.

The pipe operator is just like an entry drug to the functional world, it makes me say and wish a weird things.

So, there are #14419 and even some useful practical implications already. Should not be hard to integrate those with ts-loader.

tsconfig.json transformers integration (and so Language Service API, not just customized tsc) #14654 was declined in short term.
#11976 is discussing a Language Service plugins, which looks like a linting only tools.
#16607 proposing extension of those plugins to the transformers.

@felixfbecker

This comment has been minimized.

Copy link

commented Nov 1, 2017

@PublicParadise or just use lodash's flow or Rambda's pipe?

anyway, this would be so awesome to see to be supported in TS. I love the functional patterns JS supports (especially with TS' type inference), but some patterns don't read very nice. This would be huge as big TS libraries like RxJS and IxJS are moving towards point-free functional composition over prototype extension/inheritance, it makes for way better tree shaking and support for custom operators.

@AlexGalays

This comment has been minimized.

Copy link

commented Nov 2, 2017

@felixfbecker You mean ramda's pipe? I need to try again but historically, ramda being a JS-first lib, it's very dynamic and hard to type (like lodash), compounded by the fact TS used to have a lot of trouble inferring from function return values (it may have been fixed recently, but not sure)
I don't use lodash as it's poorly designed, mixing mutable and immutable functions in one big namespace.

@felixfbecker

This comment has been minimized.

Copy link

commented Nov 2, 2017

@tycho01

This comment has been minimized.

Copy link
Contributor

commented Nov 2, 2017

It actually works decently well if your functions and chains are not super crazy

Let me qualify 'not super crazy' there: things break down if your functions have generics (see types/npm-ramda#86), e.g. R.pipe(R.identity).

@kitsonk

This comment has been minimized.

Copy link
Contributor

commented Nov 2, 2017

Also, let's be clear, the proposal is Stage 1. The core team are getting even more shy about introducing things before Stage 3. Decorators are part of the example. Even though they were marked as experimental, we all went ahead and enabled that flag and wrote all of our production code using them. The proposal now has bounced around and there are some fundamental breaking changes in the syntax and semantics that are going to mean we are all going to have to refactor our code which puts the core team in a tight situation, because if they only support the final syntax than everyone is broken the day they release it, or if they keep the legacy stuff, other changes in the compiler could make it challenging to support the two syntaxes, and eventually you want to get rid f the old stuff, but when... 💥 💥

So the best thing to do with standards based features like this, isn't to debate TypeScript's support or lack of support here, it is to find you friendly local TC39 rep and advocate that this feature is really important to you as well as participate in the proposal conversation linked to above on GitHub. The quicker the semantics get resolved and the quicker it gets to Stage 3, the quicker we can all have nice things!

@Toxicable

This comment has been minimized.

Copy link

commented Nov 5, 2017

Now that rxjs has lettable operators this would be an even more awesome feature to have in Typescript
https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md

@marty-wang

This comment has been minimized.

Copy link
Member

commented Nov 8, 2017

Can we have someone from TS team to shed some light on this request?

@kitsonk

This comment has been minimized.

Copy link
Contributor

commented Nov 8, 2017

The have, they have tagged it ES Next and Suggestion... I can quote you chapter and verse other locations where they have commented on ES Next proposals and when and how they implement them...

A comment from them won't change anything. Do you think they are secretly working on it behind the scenes, waiting to spring it on the community? They often won't input on an issue if there is nothing to add... There is nothing to add to what has already been said.

@gajus

This comment has been minimized.

Copy link

commented Jul 30, 2018

It has landed in Firefox under --enable-pipeline-operator compile flag.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator

@lukeshiru

This comment has been minimized.

Copy link

commented Jul 30, 2018

A minute of silence for the fallen hero, the bind operator ::, closed in favor of the evil |> 😢

@AlexGalays

This comment has been minimized.

Copy link

commented Jul 30, 2018

Well I guess it's in the eye of the beholder, as I prefer |> :D

@zpdDG4gta8XKpMCd

This comment has been minimized.

Copy link

commented Jul 31, 2018

hail to the king!

@AlexGalays

This comment has been minimized.

Copy link

commented Aug 2, 2018

And here I thought it was a pipe dream

@babakness

This comment has been minimized.

Copy link

commented Aug 9, 2018

Pipeline is essentially a simple use case of the Identity Monad. Also, pipe is usually compose in reverse whereas pipeline is more like a pipe that is invoked right away.

Anyway, looking forward to seeing this in Typescript.

@BenBeattieHood

This comment has been minimized.

Copy link

commented Aug 9, 2018

@graingert

This comment has been minimized.

Copy link
Author

commented Aug 9, 2018

@zpdDG4gta8XKpMCd

This comment has been minimized.

Copy link

commented Aug 9, 2018

the idea of infix functions for typescript is almost as old as typescript: #2319

@aminpaks

This comment has been minimized.

Copy link

commented Aug 9, 2018

I know a lot of people want this really bad, but I believe TypeScript should not implement any extra operator as long as they are not in stage 3. Things can change and of course there are some exceptions.

@BenBeattieHood

This comment has been minimized.

Copy link

commented Aug 9, 2018

I think it would be worth trying as a compiler transformer, just to allow the community to explore the idea, and to measure popularity. It's a well-defined feature in other functional languages, so might be quite safe to explore.

@mAAdhaTTah

This comment has been minimized.

Copy link

commented Aug 10, 2018

@BenBeattieHood We're in the process of implementing this in babel, so you'll be able to test it there. If you do test it in a compiler transformer, definitely take a look at the current proposals, as there are a few forms of the pipeline operator we're considering.

@MeirionHughes

This comment has been minimized.

Copy link

commented Aug 10, 2018

I think it would need a lot of thought about how its used; specifically with regard to typing things like:

function where<T>(predicate: (x: T) => boolean) {
  return function* (items: Iterable<T>): Iterable<T> {
    for (const item of items) {
      if (predicate(item)) {
        yield item;
      }
    }
  };
}

[1, 2, 3] |> where(x=>x> 1)

at the moment with where(x => x > 1)([1,2,3]) it cannot infer what x is. the above is one reason I was hoping the :: op would win out, because it (at first glance) seems a lot easier for typescript to infer what this is

@AlexGalays

This comment has been minimized.

Copy link

commented Aug 10, 2018

Or we can see it another way: should it get released, It will prioritize some of the inference issues TS has 👍

@Bnaya

This comment has been minimized.

Copy link

commented Aug 10, 2018

If you follow the spec and babel news, the spec is not set yet. There are 2 proposals. Im sure the typescript team will add support when the spec is finalized

@tycho01

This comment has been minimized.

Copy link
Contributor

commented Aug 10, 2018

Infix functions ftw

iirc JS calls these "methods".

@masaeedu

This comment has been minimized.

Copy link
Contributor

commented Sep 2, 2018

iirc JS calls these "methods"

@tycho01 Your comment is probably tongue in cheek, but I think these aren't quite the same thing. You can't just export a binary function from somewhere and apply it infix to two values; knowledge about every function that's ever going to manipulate the value must be grafted onto the value itself, either as a direct property or on the prototype. This is pretty inconvenient in practical scenarios.

@gajus

This comment has been minimized.

Copy link

commented Dec 6, 2018

@BenBeattieHood We're in the process of implementing this in babel, so you'll be able to test it there. If you do test it in a compiler transformer, definitely take a look at the current proposals, as there are a few forms of the pipeline operator we're considering.

Babel parser now supports smart pipeline proposal.

babel/babel#8289

@linguofeng

This comment has been minimized.

Copy link

commented Mar 14, 2019

Any updates?

@kitsonk

This comment has been minimized.

Copy link
Contributor

commented Mar 14, 2019

Any updates?

🤦‍♂️ TypeScript does not implement proposal until they reach Stage 3. The pipeline operator is currently Stage 1 and has serious issues. That information has been provided multiple times in this thread.

@zpdDG4gta8XKpMCd

This comment has been minimized.

Copy link

commented Mar 14, 2019

an example of serious issues please?

@MeirionHughes

This comment has been minimized.

Copy link

commented Mar 14, 2019

maybe...

⚠ Warning: The details of the pipeline syntax are currently unsettled. There are two competing proposals under consideration.

@kitsonk

This comment has been minimized.

Copy link
Contributor

commented Mar 14, 2019

Yes, that is what I consider a serious issue.

@RyanCavanaugh

This comment has been minimized.

Copy link
Member

commented Mar 15, 2019

Going to lock this one since all threads in Waiting for TC39 status tend to just go in circles.

Ping!

@microsoft microsoft locked and limited conversation to collaborators Mar 15, 2019

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
You can’t perform that action at this time.