# Fixing the Piping/Chaining/Partial Application Issue (Rev 2)

**URL:** https://discourse.julialang.org/t/fixing-the-piping-chaining-partial-application-issue-rev-2/90408
**Category:** Internals & Design
**Tags:** proposal, piping, chaining, partial-evaluation, threading
**Created:** [November 17, 2022, 3:36pm UTC](https://discourse.julialang.org/t/fixing-the-piping-chaining-partial-application-issue-rev-2/90408 "2022-11-17T15:36:57Z")
**Posts on this page:** 1
**Showing post:** 4

<div class="post-metadata">

### Author: ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)
#### Post date: [November 18, 2022, 6:24am UTC](https://discourse.julialang.org/t/fixing-the-piping-chaining-partial-application-issue-rev-2/90408/4 "2022-11-18T06:24:46Z")

</div>

Comparing this proposal to the syntax from the better-known chaining and piping packages:

* * *

## _Example from [Chain.jl](https://github.com/jkrumbiegel/Chain.jl) Readme:_

| Proposed CCS+PAS | Base Julia |
| --- | --- |
| 

```julia
df--begin
  dropmissing
  filter(:id => >(6), _)
  groupby(_, :group)
  combine(_, :age => sum)
end

```

 | 

```julia
df |>
  dropmissing |>
  x -> filter(:id => >(6), x) |>
  x -> groupby(x, :group) |>
  x -> combine(x, :age => sum)

```

 |
| [Chain.jl](https://github.com/jkrumbiegel/Chain.jl) | [DataPipes.jl](https://gitlab.com/aplavin/DataPipes.jl) |
| --- | --- |
| 

```julia
@chain df begin
  dropmissing
  filter(:id => >(6), _)
  groupby(:group)
  combine(:age => sum)
end

```

 | 

```julia
@p begin
  df
  dropmissing
  filter(:id => >(6), x)
  groupby(_, :group)
  combine(_, :age => sum)
end

```

 |
| [Pipe.jl](https://github.com/oxinabox/Pipe.jl) | [Lazy.jl](https://github.com/MikeInnes/Lazy.jl) |
| --- | --- |
| 

```julia
@pipe df |>
  dropmissing |>
  filter(:id => >(6), _)|>
  groupby(_, :group) |>
  combine(_, :age => sum)

```

 | 

```julia
@> df begin
  dropmissing
  x -> filter(:id => >(6), x)
  groupby(:group)
  combine(:age => sum)
end

```

 |
| [Underscores.jl](https://c42f.github.io/Underscores.jl/stable/) | [Hose.jl](https://github.com/FNj/Hose.jl) |
| --- | --- |
| 

```julia
@_ df |>
  dropmissing |>
  filter(:id => >(6), __) |>
  groupby(__, :group) |>
  combine(__, :age => sum)

```

 | 

```julia
@hose df |>
  dropmissing |>
  filter(:id => >(6), _) |>
  groupby(_, :group) |>
  combine(_, :age => sum)

```

 |

* * *

## _Example from [DataPipes.jl](https://gitlab.com/aplavin/DataPipes.jl) Readme_

| Proposed CCS+PAS | Base Julia |
| --- | --- |
| 

```julia
"a=1 b=2 c=3"--begin
  split
  map(_) do --
    split(_, "=")
    Symbol(it[1]) => parse(Int, it[2])
  end
  NamedTuple
end

```

 | 

```julia
"a=1 b=2 c=3" |>
  split |>
  it->map(it) do it
    it=split(it, "=")
    Symbol(it[1]) => parse(Int, it[2])
  end |>
  NamedTuple

```

 |
| [Chain.jl](https://github.com/jkrumbiegel/Chain.jl) | [DataPipes.jl](https://gitlab.com/aplavin/DataPipes.jl) |
| --- | --- |
| 

```julia
@chain "a=1 b=2 c=3" begin
  split
  map(_) do it
    it=split(it, "=")
    Symbol(it[1]) => parse(Int, it[2])
  end
  NamedTuple
end

```

 | 

```julia
@p let
  "a=1 b=2 c=3"
  split
  map() do __  
    split(__, '=')
    Symbol(__[1]) => parse(Int,__[2])
  end
  NamedTuple
end

```

 |
| [Pipe.jl](https://github.com/oxinabox/Pipe.jl) | [Lazy.jl](https://github.com/MikeInnes/Lazy.jl) |
| --- | --- |
| 

```julia
@pipe "a=1 b=2 c=3" |>
  split |>
  map(_) do it
    it=split(it, "=")
    Symbol(it[1]) => parse(Int, it[2])
  end |>
  NamedTuple

```

 | 

```julia
@>> "a=1 b=2 c=3" begin
  split
  map(it->begin
    it=split(it, "=")
    Symbol(it[1]) => parse(Int, it[2])
  end)
  NamedTuple
end

```

 |
| [Underscores.jl](https://c42f.github.io/Underscores.jl/stable/) | [Hose.jl](https://github.com/FNj/Hose.jl) |
| --- | --- |
| 

```julia
@_ "a=1 b=2 c=3" |>
  split |>
  map(it->begin
    it=split(it, "=")
    Symbol(it[1]) => parse(Int, it[2])
  end, __) |>
  NamedTuple

```

 | 

```julia
@hose "a=1 b=2 c=3" |>
  split |>
  map(_) do it
    it=split(it, "=")
    Symbol(it[1]) => parse(Int, it[2])
  end |>
  NamedTuple

```

 |

* * *

## _Examples from [Pipe.jl](https://github.com/oxinabox/Pipe.jl) Readme_

| Proposed CCS+PAS | Base Julia |
| --- | --- |
| 

```julia
a--b(_...)
a--b(it(1,2))
a--b(it[3])
(2,4)--get_angle(_,_)

```

 | 

```julia
a |> x->b(x...)
a |> x->b(x(1,2))
a |> x->b(x[3])
(2,4) |> x->get_angle(x[1],x[2])

```

 |
| [Chain.jl](https://github.com/jkrumbiegel/Chain.jl) | [DataPipes.jl](https://gitlab.com/aplavin/DataPipes.jl) |
| --- | --- |
| 

```julia
@chain a b(_...)
@chain a b(_(1, 2))
@chain a b(_[3])
@chain (2,4) get_angle(_[1],_[2])

```

 | 

```julia
@p a b(__...)
@p a b(__(1,2))
@p a b(__[3])
@p (2,4) get_angle(__[1],__[2])

```

 |
| [Pipe.jl](https://github.com/oxinabox/Pipe.jl) | [Lazy.jl](https://github.com/MikeInnes/Lazy.jl) |
| --- | --- |
| 

```julia
@pipe a |> b(_...)
@pipe a |> b(_(1, 2))
@pipe a |> b(_[3])
@pipe (2,4) |> get_angle(_[1],_[2])

```

 | 

```julia
# N/A
# N/A
# N/A
# N/A

```

 |
| [Underscores.jl](https://c42f.github.io/Underscores.jl/stable/) | [Hose.jl](https://github.com/FNj/Hose.jl) |
| --- | --- |
| 

```julia
@_ a |> b(__...)
@_ a |> b(__(1,2))
@_ a |> b(__[3])
@_ (2,4) |> get_angle(__[1],__[2])

```

 | 

```julia
@hose a |> b(_...)
@hose a |> b(_(1,2))
@hose a |> b(_[3])
@hose (2,4) |> get_angle(_[1],_[2])

```

 |

* * *

## _My Examples_

| Proposed CCS+PAS | Base Julia |
| --- | --- |
| 

```julia
[1,2,3]--map(_^2, _)
[1,2,3]--join(_, ", ")
"1"--parse(Int, _) == 1
(:a,:b)--reverse--f(_...)

```

 | 

```julia
[1,2,3] |> x->map(x->x^2, x)
[1,2,3] |> x->join(x, ", ")
([1,2,3] |> x->parse(Int, x)) == 1
(:a,:b) |> reverse |> x->f(x...)

```

 |
| [Chain.jl](https://github.com/jkrumbiegel/Chain.jl) | [DataPipes.jl](https://gitlab.com/aplavin/DataPipes.jl) |
| --- | --- |
| 

```julia
@chain [1,2,3] map(x->x^2, _)
@chain [1,2,3] join(", ")
@chain("1", parse(Int, _)) == 1
@chain (:a,:b) reverse f(_...)

```

 | 

```julia
@p [1,2,3] map(_^2)
@p [1,2,3] join(__, ", ")
@p("1", parse(Int)) == 1
@p (:a,:b) reverse f(__...)

```

 |
| [Pipe.jl](https://github.com/oxinabox/Pipe.jl) | [Lazy.jl](https://github.com/MikeInnes/Lazy.jl) |
| --- | --- |
| 

```julia
@pipe [1,2,3] |> map(x->x^2, _)
@pipe [1,2,3] |> join(_, ", ")
@pipe("1" |> parse(Int, _)) == 1
@pipe (:a,:b) |> reverse |> f(_...)

```

 | 

```julia
@>> [1,2,3] map(x->x^2)
@> [1,2,3] join(", ")
@>>("1", parse(Int)) == 1
# N/A

```

 |
| [Underscores.jl](https://c42f.github.io/Underscores.jl/stable/) | [Hose.jl](https://github.com/FNj/Hose.jl) |
| --- | --- |
| 

```julia
@_ [1,2,3] |> map(_^2, __)
@_ [1,2,3] |> join(__, ", ")
@_("1" |> parse(Int, __)) == 1
@_ (:a,:b) |> reverse |> f(__...)

```

 | 

```julia
@hose [1,2,3] |> map(x->x^2, _)
@hose [1,2,3] |> join(_, ", ")
@hose("1" |> parse(Int, _)) == 1
@hose (:a,:b) |> reverse |> f(_...)

```

 |

* * *

## _Of interest:_

Many of these packages implement single-underscore `_` and double-underscore `__`, each with a meaning, if not equal to, approximating:

1. Placeholder to specify argument position for partial function evaluation (“tight currying”)
2. Return value of last element in execution chain (“loose binding”)

It’s fairly confusing to identify which is which, because they _seem_ to have similar meanings in these contexts, they look almost identical, and different packages swap their symbols or give them slightly different meanings.

In this proposal, to avoid confusing the two, I call #2 `it` reflecting the analogous role of the pronoun in the English language for method chaining, and I leave #1 as the most basic Scala-style argument placeholder `_` for partial function evaluation.

My proposal intends to keep the behavior rules as simple and consistent as possible, to create a syntax that composes well, instead of fragile and complicated rules.

* * *

## _Some color on the word “it”_

Method chaining is a common idiom in natural language too. In some instances, a sequence of methods is directly composable. For example (in pseudo-English):

> Cat: Put on lap. Inspect fur. Find flea. Pull off. Put in soapy water.

In these instances, the pronoun “it” can be implied. In other instances, when methods are not directly composable and minor glue logic is necessary to ready an object for the next step in the chain, we must make “it” explicit:

> Baby: Pick up. Lift its head above its legs. Put butt on your arm. Rock to sleep.

Notice that without the glue employing “it,” the composition might not work very well.

The call chain syntax I propose here intends to handle these cases, and uses the same keyword “it” for the exact same reasons.

Of course, there are more sophisticated scenarios where each object must be specified at each step:

> Pot, oats, milk: Put the pot on the stove. Put oats in the pot. Pour milk in the pot. Turn on the stove.

For these more general (and more verbose) scenarios, we already have lambdas and named functions.

---

_[View the full topic](https://discourse.julialang.org/t/fixing-the-piping-chaining-partial-application-issue-rev-2/90408)._
