Skip to content

Customizable/query/computation/monad pipes (railway-oriented programming) #312

@js-choi

Description

@js-choi

This is a spinoff of #311 (comment) and #159.

  • In many contexts, such as query building, pipes oftentimes may need to do repetitive computations or queries. These contexts include:
  • 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 (Deeply nested expressions A(B(C(D(E(…), …), …), …), …) vs. temp variables #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 do expressions and async do expressions.
    • 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 Placeholder name, eg %name #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(#), where maybe is a variable containing a “maybe monad” that would cause the pipeline to evaluate f(#) only if intOrNullValue is not nullish.
    • Context pipes could create complex query scaffolding at each step of the pipeline.
    • Context pipes would cover optional pipes (Optional chaining support for the pipeline operator? #159 and Optional Hackpipes proposal #198), which are just context pipes using a maybe monad.
    • They would also support custom contexts like LINQ query building.
  • 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    follow-on proposalDiscussion about a future follow-on proposal

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions