Repository: tweag/nickel (rust)

Issue:
Pipe operator (not the one we have, but reverse application)
**Is your feature request related to a problem? Please describe.**
It often happens that you want to apply several operation to a value in a row. In the current state, this leads to a verbose sequence of let in:

```
let list = [1, 2, 3, 4, 5] in
let list = lists.map (fun x => x + 1) list in
let list = lists.filter (fun x => x > 2) list in
lists.partition (fun x => x % 2 == 0) list
```

**Describe the solution you'd like**
A "reverse application operator", or pipe operator, as OCaml's `|>`. We already have `|` quite overloaded, so I propose to use something else, like `$>` (but that's just a suggestion on the fly). We could have the symmetric sibling `<$` too. This would have the following semantics: `x $> f` is `f x`. It is left associative, such that `x $> f $> g $> h` is `h (g (f (x)))`. The example becomes:

```
[1,2,3,4,5]
$> lists.map (fun x => x + 1)
$> lists.filter (fun x => x > 2)
$> lists.partition (fun x => x % 2 == 0)
```

**Describe alternatives you've considered**

- Point-free notation as in Haskell. Say we have composition operator `.` such that `f . g ` is `fun x => f (g x)`, then that would give:

  ```
  (lists.partition (fun x => x % 2 == 0)
  . lists.filter (fun x => x > 2)
  . lists.map (fun x => x + 1)) [1, 2, 3, 4, 5]
  ```

  However, it reverses the order of transformations (flowing right-bottom to top-left). The pipe operator is usually better at concisely expressing a sequence of transformations, IMHO.


Pull request:
[Syntax]Pipe operator (reverse application)
Close #455. Add a reverse application operator to nicely write a sequence of transformation as something like a Unix pipeline.

Usually you want this operator to not take precedence too much, to avoid parentheses around the initial expression such as `x * 2 |> nums.pow 2` and such that it can also serve as a Haskell's `$`, just reversed. I put it at a similar precedence level as in OCaml, where it takes higher precedence than boolean operators like `&&` and `||`. However Haskell's `$` and Elm `|>` take the lowest precedence of all operators, even than boolean ones. I'm not sure how much it matters: should `foo && bar |> stuff |> otherstuff` be interpreted as `foo && (bar |> stuff |> otherstuff)` or `(foo && bar) |> stuff |> otherstuff`? I don't have a strong opinion, although I arbitrarily prefer the first option, as is implemented in this PR.

Patch:
diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop
index 17fb169e..94fda32f 100644
--- a/src/grammar.lalrpop
+++ b/src/grammar.lalrpop
@@ -398,6 +398,8 @@ InfixExpr: RichTerm = {
     #[precedence(level="6")] #[assoc(side="left")]
     <t1: WithPos<InfixExpr>> "&" <t2: WithPos<InfixExpr>> =>
         mk_term::op2(BinaryOp::Merge(), t1, t2),
+    <t1: WithPos<InfixExpr>> "|>" <t2: WithPos<InfixExpr>> =>
+        mk_app!(t2, t1),
 
     #[precedence(level="7")] #[assoc(side="left")]
     <t1: WithPos<InfixExpr>> "<" <t2: WithPos<InfixExpr>> =>
@@ -576,6 +578,7 @@ extern {
         "fun" => Token::Normal(NormalToken::Fun),
         "import" => Token::Normal(NormalToken::Import),
         "|" => Token::Normal(NormalToken::Pipe),
+        "|>" => Token::Normal(NormalToken::RightPipe),
         "->" => Token::Normal(NormalToken::SimpleArrow),
         "=>" => Token::Normal(NormalToken::DoubleArrow),
         "#" => Token::Normal(NormalToken::Hash),
diff --git a/src/parser/lexer.rs b/src/parser/lexer.rs
index 9f567b76..92d98e6d 100644
--- a/src/parser/lexer.rs
+++ b/src/parser/lexer.rs
@@ -130,6 +130,8 @@ pub enum NormalToken<'input> {
     Import,
     #[token("|")]
     Pipe,
+    #[token("|>")]
+    RightPipe,
     #[token("->")]
     SimpleArrow,
     #[token("=>")]


Test patch:
diff --git a/tests/pass/builtins.ncl b/tests/pass/builtins.ncl
index 1e2bac24..a791ec2e 100644
--- a/tests/pass/builtins.ncl
+++ b/tests/pass/builtins.ncl
@@ -19,4 +19,12 @@ let Assert = fun l x => x || %blame% l in
   let r = {a=(inj 1),b=(cat "a" "b")} in
   %deepSeq% r (r.a.b) == 3 | #Assert) &&
 
+([1,2,3]
+ |> lists.map (fun x => x + 1)
+ |> lists.filter (fun x => x > 2)
+ |> builtins.serialize `Json
+ |> builtins.deserialize `Json
+ == [3,4]
+ | #Assert) &&
+
 true