proposal-pipeline-operator
proposal-pipeline-operator copied to clipboard
Customizable/query/computation/monad pipes (railway-oriented programming)
This is a spinoff of https://github.com/tc39/proposal-pipeline-operator/issues/311#issuecomment-2788293430 and #159.
- In many contexts, such as query building, pipes oftentimes may need to do repetitive computations or queries. These contexts include:
- Optional chaining (#159 and #198).
- Observables.
- Database queries (e.g., SQL, LINQ).
- Parser combinators.
- Data sources like Facebook’s Haxl.
- (I wrote this issue in a hurry, so the above use cases need specific code examples later, maybe from my old context blocks proposal.)
- Railway-oriented programming:
- Many of these contexts share a common pattern.
- Their computations generally run on a “happy path”.
- But there is an alternative track that needs to be handled differently (such as expected operational exceptions).
- This is sometimes called “railway-oriented programming” in F# and C#.
- In JavaScript…
- If the pipeline (or “railway”) is simple, with only one (synchronous) happy path and no special processing on each step, then simple assignment to temporary variables works (#311).
- If there is an alternative non-happy path, any asynchrony is involved, or there is special processing on any state, then the pipeline cannot use simple variable assignment.
- In that case, it must use nested callbacks / continuations / IIFEs (forming deeply nested pyramids of doom.
- Each consecutive inner callback is a step in the pipeline.
- The parameter of each consecutive inner callback is the argument to its following steps.
- Haskell’s solution (as well as those of many other functional programming languages) includes functor applicators, monads, monoids, arrows, and so on.
- F#’s solution:
- F# has solved this using query expressions and its other computation expressions.
- F# uses its query expressions to support LINQ, as well as LINQ-like syntax in other contexts.
- In actuality, F# query/computation expressions are a clever way to implement monads (and applicator functors and monoids).
- There is a deep relationship between Hack pipes and F#’s query expressions and computation expressions (and the LINQ queries they came from, and do-notation, and so on).
- F# query/computation expressions flatten deeply nested callbacks (nested continuations or immediately invoked functions / IIFEs), taking their parameters and lining them up in a single linear pipeline.
- This is indeed pretty similar to the goals of the ES pipe operator. Even the name “railway-oriented programming” sounds like a more complicated version of “pipeline-oriented programming”.
- Years ago, I made an old, abandoned ES “context blocks” proposal for blocks of based on IIFEs.
- ES context blocks were inspired by F# computation expressions and other languages’ monadic do-notation.
- ES context blocks would allow people to use LINQ-like syntax generically in custom contexts, binding variables to each step of a custom sequence of computations or queries.
- ES context blocks would encompass the inactive
doexpressions andasync doexpressions. - ES context blocks would be extensible by the user for creating pipelines in other contexts, like observable/signal chaining, file processing, database queries, parsers, or anything else involving deeply nested callbacks / continuations.
- They would functionally act quite similar to the idea in #274.
- The pipe operator could hypothetically be extended into a customizable “context pipe operator” that uses the same context-block monad infrastructure as context blocks.
- These “context” pipe operators that apply a monad or some other kind of custom “context” to a pipe, like
intOrNullValue |maybe> f(#), wheremaybeis a variable containing a “maybe monad” that would cause the pipeline to evaluatef(#)only ifintOrNullValueis not nullish. - Context pipes could create complex query scaffolding at each step of the pipeline.
- Context pipes would cover optional pipes (#159 and #198), which are just context pipes using a maybe monad.
- They would also support custom contexts like LINQ query building.
- These “context” pipe operators that apply a monad or some other kind of custom “context” to a pipe, like
- But, this is for a far-future add-on proposal.
- These are just half-formed ideas right now.
- The Committee has become very generally chilly towards any new syntax.
- This has become especially uncertain with the recent JS0/JSSugar proposal.
- There is no way that we can feasibly advance a more complicated pipe, let alone context pipes or context blocks.
- That’s much of why I haven’t spent time on them.
- If you want to discuss LINQ-like “customized” context pipelines here, or “context blocks”, or monads in general, feel free to discuss them here.