Skip to content

Pipe (|) operator has incorrect precedence (too tight) #27415

Closed as not planned
Closed as not planned

Description

@thorn0

🐞 bug report

Affected Package

The issue is caused by package @angular/core

Is this a regression?

Yes, the previous version in which this bug was not present was: 1.x

Description

AngularJS: a ? b : c | x means (a ? b : c) | x.

Angular: a ? b : c | x means a ? b : (c | x).

It is unexpected. E.g. see #19657, prettier/prettier#5586.

Who would write a ? b : c | x meaning a ? b : (c | x) given that a ? b | x : c even is not a valid expression? This does not look like an intentional design decision, more like a mistake (bug).

The natural expectation is that the pipe in the end of an expression should be applied to the whole expression, even if there is no parentheses. According to the docs, pipes are a way to write display-value transformations that you can declare in your HTML. They are perceived as external to expressions, something that modifies the expression without really being part of it. For this intuition to be valid, pipes must have the lowest precedence. The current behavior breaks the intuition for no reason, without adding any value.

🔬 Minimal Reproduction

Activity

  1. Mapache commented on Dec 3, 2018

    @Mapache

    The behavior you describe for AngularJS looks like the bug, as the | operator is supposed to have higher precedence than ?: even in plain JavaScript.

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

    Personally, I recommend using explicit grouping when chaining lots of operators together, as a form of executable documentation that makes programmer intent clear instead of forcing the reader to rely on their memory of precedence between distinct families of operators.

  2. thorn0 commented on Dec 3, 2018

    @thorn0
    ContributorAuthor

    The | operator in JS has nothing to do with the | operator in Angular expressions.

  3. added this to the needsTriage milestone on Dec 3, 2018
  4. trotyl commented on Dec 4, 2018

    @trotyl
    Contributor

    Pipe in template is an equivalent of JavaScript pipeline operator in semantic, the Angular behavior is correct, where a ? b : c | x means a ? b : (c | x).

  5. thorn0 commented on Dec 4, 2018

    @thorn0
    ContributorAuthor

    @trotyl

    1. Why isn't a ? b | x : c a valid expression then?
    2. Any practical reasons behind this decision (to align with those proposals)? Was its motivation like 'one day |> might be standardized and become really widespread, people will get used to its precedence, so the precedence of our | might become confusing'? I would buy this justification if Angular's operator looked the same (|>). But it doesn't look the same, why would anybody expect it to have the same precedence?
  6. trotyl commented on Dec 4, 2018

    @trotyl
    Contributor

    @thorn0

    1. You're making a lexer hazard here, b | x : c could be recognized as pipe x with additional pipe parameter c. There may need to be some better error handling, but it's the syntax ambiguity rather than precedence issue.
    2. The mental model for Pipe is a syntax sugar for function calls with better ergonomics, which is equivalent to JavaScript pipeline operator. The template language is a JavaScript subset with additional template utils, there's no reason to violates JavaScript deliberately if already conforming.
  7. gkalpak commented on Dec 4, 2018

    @gkalpak
    Member

    TBH, I personally find that Angular's | has enough differences with |> anyway (e.g. appearance 😁, order of arguments), that keeping one characteristic in sync (precedence) in the name of "mental model equivalence" is a weak argument. (Even more so that JS already has a | operator with totally different semantics, so we've missed the "reasonable equivalence" train, I'm afraid 😛).

    In my mind, Angular's | is more like UNIX pipes and in that sense I would also expect them to have lower precedence (from a mental model pov).
    m2c

    FWIW, I would always parenthesize such complex expressions, regardless of the precedences 😉

  8. trotyl commented on Dec 4, 2018

    @trotyl
    Contributor

    @gkalpak Have had a check with the bash operator precedence, where pipe do have higher precedence than logical operators (&& or ||, sadly there's no ?: in bash):

    echo "AbC" && echo "Def" | sed 's/[^a-z]//g'

    Would be parsed as

    echo "AbC" && (echo "Def" | sed 's/[^a-z]//g')

    rather than

    (echo "AbC" && echo "Def") | sed 's/[^a-z]//g'
  9. thorn0 commented on Dec 4, 2018

    @thorn0
    ContributorAuthor

    @trotyl And what about a | x ? b : c? My point about a ? b | x : c is that it is inconsistent. If we want to apply a pipe to some operand of a ? b : c, why do we have to parenthesize a and b, but not c?

    Normally, if an operator O1 has a higher precedence than the operator O2 it means that we can use expressions with O1 as operands of O2 without parentheses. It's almost not the case for Angular's | and ?:. So it's a really strange precedence choice, I must conclude.

  10. trotyl commented on Dec 4, 2018

    @trotyl
    Contributor

    @thorn0 It's just a parser implementation bug unrelated to ternary operator, even a | x + 1 would produce same error. The current scenario is that the left-hand side of pipe can be any expression, while right-hand side must be purely pipe name with optional pipe parameters.

    The current Angular template parser is just a poorly hand-written recursive descent implementation, any issue is expected to happen.


    EDIT: checked pipeline operator, it should be correct one, a |> x + 1 would be translated to var _a; (_a = a, (x + 1)(_a)), meaning that whole right-hand side is considered to be the function to invoke. But Angular pipe cannot be dynamically computed, so the restriction make sense.


    EDIT2: checked again, a |> x ? b : c would become var _a; (_a = a, x(_a)) ? b : c;, which is correctly handled (ternary having lower precedence), still seems to be an Angular bug.

  11. mhevery commented on Dec 4, 2018

    @mhevery
    Contributor

    Yes, you are right the precedence is wrong :-(

  12. 3 remaining items

  13. added
    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
    and removed on Oct 1, 2020
  14. changed the title [-]Pipes vs ternaries: unexpected grammar difference between Angular and AngularJS[/-] [+]Pipe (`|`) operator has incorrect precedence (too tight)[/+] on Sep 17, 2021
  15. petebacondarwin commented on Oct 15, 2021

    @petebacondarwin
    Contributor

    This is being tracked in the aggregate issue of #43485 for which we need to create a project proposal.

  16. jelbourn commented on Jul 10, 2024

    @jelbourn
    Contributor

    Duplicate of #43485

    Closing this as a duplicate of #43485 even though they're not exactly the same thing because it's the ultimate direction things will likely move. At this point, we think it's far too breaking of a change to change the operator precedence for pipes. Any changes here would be part of a much larger (and hypothetical) shift in the way Angular template expressions work.

  17. angular-automatic-lock-bot commented on Aug 10, 2024

    @angular-automatic-lock-bot

    This issue has been automatically locked due to inactivity.
    Please file a new issue if you are encountering a similar or related problem.

    Read more about our automatic conversation locking policy.

    This action has been performed automatically by a bot.

  18. locked and limited conversation to collaborators on Aug 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions