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

Pattern Matching in Pipes #305

Closed
triyanox opened this issue Jul 24, 2024 · 7 comments
Closed

Pattern Matching in Pipes #305

triyanox opened this issue Jul 24, 2024 · 7 comments

Comments

@triyanox
Copy link

I have a request/proposal to consider. I recognize it would almost certainly be made into its own proposal, but since it's deeply related to |> I wanted to raise it here first. The |> operator provides a clean way to chain operations, but it lacks built-in support for handling different data shapes or cases within the pipeline. I propose extending the pipe operator to support inline pattern matching, which would enhance its expressiveness and flexibility.

I propose adding a match% keyword that can be used within a pipe chain to perform pattern matching. Here's an example of how it might look:

data |> match% {
  case [head, ...tail] => processHead(head) |> concat(tail),
  case { type: "user", name } => greetUser(name),
  case { type: "admin" } => adminDashboard(),
  default => handleUnknownData
}

In this syntax, match% initiates pattern matching within the pipe, with case clauses defining patterns to match against. The right side of => in each case can include further piping. This approach allows complex branching logic within pipes without breaking the flow, makes data transformation pipelines more declarative and easier to understand, combines the power of pattern matching with the composability of pipes, and provides a clean way to handle different data shapes or error cases in pipelines.

The pattern matching in pipes could also support destructuring, as shown in this example:

fetchUserData()
|> match% {
  case { name, age, friends: [bestFriend, ...otherFriends] } =>
    % |> formatUserProfile
      |> addBestFriendInfo(bestFriend)
      |> listOtherFriends(otherFriends),
  case { error } =>
    % |> logError
      |> displayErrorMessage
}

It could also integrate well with async operations:

fetchData()
|> await%
|> match% {
  case { status: "success", data } =>
    % |> processData
      |> updateUI,
  case { status: "error", message } =>
    % |> logError
      |> showErrorNotification
}

And support guard clauses for more complex conditions:

validateUser(user)
|> match% {
  case { age } when age >= 18 =>
    % |> grantAccess
      |> logAdultUser,
  case { age } when age < 18 =>
    % |> restrictAccess
      |> notifyParents,
  default => handleInvalidUser
}

While this proposal offers significant benefits, there are challenges to consider, including syntax complexity, performance, integration with existing patterns, and tooling support. To move forward, we should gather community feedback, develop a detailed specification, create a basic implementation, and discuss with the TC39 committee.

I welcome thoughts and ideas on this proposal. How do you see this fitting into JavaScript? Any overlooked use cases or challenges?

@ljharb
Copy link
Member

ljharb commented Jul 24, 2024

i'm confused, can't you already do this with match (%) { … }, with no further changes to pipeline or pattern matching?

@triyanox
Copy link
Author

@ljharb You're right about match (%) { ... } covering most of what I proposed. My suggestion was mainly about tighter integration with pipe syntax, potentially offering more concise notation (match%) and implicit % in each case. However, these benefits might not justify a syntax change. What do you think ?

@ljharb
Copy link
Member

ljharb commented Jul 24, 2024

Having an implicit placeholder is expressly undesirable, and is part of why the pipeline proposal is in its current form.

@triyanox
Copy link
Author

triyanox commented Jul 25, 2024

Ah I see yes the match (%) { ... } approach is indeed more consistent with the js design principles so I totally I agree for sure 👍

@snatvb
Copy link

snatvb commented Aug 2, 2024

I think it's syntax overload.
If we get match as an expression (I think it should be a different proposal), we can do this

takeUser(id)
  |> (u) => match(u) ...

creating a separate syntax for such narrow solutions is the road to syntax hell that C++ follows

@bogdanbiv
Copy link

bogdanbiv commented Aug 5, 2024

As the last comment says, this is syntax overload. We don't want several ways to achieve one thing (we'd get another Perl, which is horror). If the match proposal gets accepted, this issue will be covered. If that doesn't get accepted, no one will want a syntax that looks like another language but actually works only for pipelines in ES202x.

I recommend closing this issue and to follow up on the two proposals separately. Well this one obviously and https://github.com/tc39/proposal-pattern-matching

image

Originally posted by @yordis in #91 (comment)

@tabatkins
Copy link
Collaborator

Yup, this is indeed a completely separate feature (already being addressed by a different proposal) that doesn't require integration with pipeline at all. I'm gonna go ahead and close this.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 13, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants