Fixing the Piping/Chaining Issue

On the surface, there is quite a bit of difference between the original proposal (OP) and the JuliaSyntax PR proposal (JS). Perhaps it works out that the overall behavior is roughly the same—I haven’t figured that out yet.

Let’s take a look at an example from JS. This expression,

x  />  f(y)  \>  g(z)

is parsed as

# S-expression pseudo-code:
(chain x (/> f y) (\> g z))

which gets lowered to

chain(x, fixbutfirst(f, y), fixbutlast(g, z))

So, in JS, /> and \> are effectively unary operators that take in a function call on the right-hand side (RHS). They do not operate on the code on their left-hand side (LHS). This is in contrast to OP, where the front/back fix operators are binary operators that operate on the LHS and the RHS.

Another difference to note is that JS has an implicit piping operation built in (piping is not currying!), which is expressed by the chain function in the parsed and lowered code. OP on the other hand, does not exactly have a piping semantic, although it kind of sneaks in by the way that function calls and front/back fix operators are parsed.

The above expression is parsed rather differently in OP. OP would produce the following after parsing:

((x  />  f)(y))  \>  g)(z) 

Let’s re-write that using S-expressions, for easier comparison with JS:

((((/> x f) y) \> g) z)

That’s quite a bit different from what JS is doing. Although we should be careful to note that /> is different in OP and JS (more on that later).

Another important difference is that when you are creating a Fix* type, OP produces nested types, whereas JS produces just one type. Consider this function:

foo(x, y, z) = x, y, z

If we want to fix the first two arguments, we would do this in OP:

:b /> :a /> foo

which returns this:

FixFirst((FixFirst(foo, :a), :b)

If we want to fix the first two arguments in JS, we would do this:

\> f(:a, :b)

which calls this:

fixbutlast(f, :a, :b)

which produces a single (non-nested) FixButLast object.

Actually, this example shows that, in JS, \> is treated basically opposite to the way it is treated in OP. In JS, (/> f x) means “fix every argument except the first”, whereas in OP, (/> x f) means “fix only the first argument, and leave all the others free”. So the JS implementation actually behaves a lot more like “front pipe” and “back pipe” than like “front fix” and “back fix”. I think it would be semantically cleaner to just have “front pipe” and “back pipe” than to have “fix every argument but the first” and “fix every argument but the last”, with an implicit chain thrown in.

3 Likes