Want to contribute? Fork us in GitHub!

Pipe to the Object Above

In 𝜑-calculus, a formation may be applied on the spot: the expression ⟦a ↦ 7, x ↦ ∅⟧(x ↦ 42) forms an object with one void attribute and immediately supplies the argument. EO had no way to say this. A formation first had to receive a name, and then a separate application had to repeat that name. Recently we closed the gap with a new syntax element: a line that starts with |, which we call a pipe.

One Character, Pointing Up

A pipe line applies arguments to the object declared just above it, at the same indentation:

[x] > foo
  x.plus x > @
| 5 > foo5

The formation foo doubles its argument; the pipe applies 5 to it and names the result foo5. The | reads as an arrow pointing up: “take the object above, give it 5, call the result foo5.” The semantics is untouched — the pipe desugars into a plain application, and the program above compiles to exactly the same XMIR as:

[x] > foo
  x.plus x > @
foo 5 > foo5

Sugar, pure sugar. So why bother?

Lambdas, Finally Callable

Because the name may not exist. The >> suffix auto-names an abstract object when a meaningful name would add nothing, and the generated name is deliberately untypeable. Until now, such an object could only be passed somewhere as an argument; it could not be applied, since application requires a name to repeat. The pipe needs no name:

[a b] >>
  a.plus b > @
| 2 3 > pair

This is a lambda expression applied at the place of its definition — what the original issue asked for: anonymous formations working as functions in Haskell or lambdas in Java.

Chains, Verticals, Dots

Consecutive pipes stack into left-associated applications:

[x] > foo
  x > @
| 1 >>
| 2 > result

Here result is (foo 1) 2. Each pipe in a chain is an object of its own and must carry a name, because the next pipe refers to its predecessor by it — >> does the job when you don’t care.

When arguments don’t fit on one line, the pipe takes them vertically, exactly as any application head does:

[x y] > add
  x.plus y > @
| > sum
  2
  3

And since a pipe application is a complete value, a .method may follow it:

[x] > foo
  x > @
| 5
.neg > y

The Rules

The pipe is picky about its predecessor. It must be a formation or another pipe, and it must be named — either explicitly with > name or automatically with >>:

6 > six
| 5 > n

This is rejected: six is a number, not a formation. A pipe with nothing above it is rejected too, and so is a pipe after a .method line: once the dot has dispatched an attribute, the formation is no longer the object in hand.

What It Buys

The runtime library already uses pipes, mostly for one pattern: a local recursive helper, defined and called in one breath. This is how number.power computes an integer exponent:

[b n] >> ipow
  if. > @
    n.eq 0
    1
    if.
      is-integer. (n.div 2)
      half.times half
      b.times
        ipow b (n.minus 1)
    ipow b (n.div 2) > half
| base (abs expo)

The helper stays anonymous to the enclosing scope, yet recursion works: >> ipow gives the formation a file-local handle, the body calls itself by it, and the pipe fires the first call with the actual arguments. Definition and invocation sit together; the reader never hunts across the file for the place where ipow is used.

In 𝜑-calculus, formation-with-application was there all along. Now EO can write it too, without inventing a name for the middle.

That’s all for today.