Skip to content

Instantly share code, notes, and snippets.

@damncabbage
Last active December 8, 2015 23:32
Show Gist options
  • Save damncabbage/e42dea273ed6d7c6c753 to your computer and use it in GitHub Desktop.
Save damncabbage/e42dea273ed6d7c6c753 to your computer and use it in GitHub Desktop.

Assume concat is some function sticking two strings together, and uppercase is... well:

Haskell (argument application):

> let a |> f = f a

> "def"
    |> (\x -> concat "abc" x)
    |> uppercase                      -- (\x -> uppercase x) if you want to be explicit.
    |> (\x -> concat "Alphabet: " x)
"Alphabet: ABCDEF"

-- (For completeness, the shortcut version: auto-currying means calling a
--  two-arg function with one arg means you get back a function that expects one arg.)
> "def"
    |> concat("abc")
    |> uppercase
    |> concat("Alphabet: ")
"Alphabet: ABCDEF"

JS/ES7 with https://github.com/mindeavor/es-pipeline-operator (argument application)

> "def"
    |> x => concat("abc", x)
    |> uppercase                     // (x => uppercase x) if you want to be explicit.
    |> x => concat("Alphabet: ", x)
Alphabet: ABCDEF

Elixir (syntax macro):

> "def" |> Foo.concat("abc") |> Foo.uppercase |> Foo.concat("Alphabet: ")
"DEFABCAlphabet: "

Haskell and the proposed ES7 change, for each of the "steps", calls the function on the right with the value from the left. (Haskell just has an "auto-currying" shortcut [the second example], that makes this look nicer.)

Elixir does a syntax shuffle with the |> macro (not function); it takes what's being passed along and puts into the first slot, and moves the other arguments along. It's not possible to do this "in-universe" without breaking out reflection to access and monkey with arguments.

Breaking it down

Further breaking down the JavaScript example from the proposal:

var result = "hello"
  |> doubleSay
  |> capitalize;

You can think of x |> myFunction as some function called |> with two arguments, eg. pipeOp(x, myFunction).

So then "hello" |> doubleSay |> capitalize could be thought of as: pipeOp(pipeOp("hello", doubleSay), capitalize).

If we assume that pipeOp = function(val, func) { return func(val) };
... then you get: capitalize(doubleSay(x))
... And therefore: result === "HELLOHELLO"

(Whether the actual implementation matches the above is irrelevant; that's how it works when you use it.)

This is in contrast to Elixir where it's doing something with the tokens in the programming language itself, and inserting an argument in the first position, as opposed to the above where it's "just functions".

@madlep
Copy link

madlep commented Dec 7, 2015

FWIW, you can do the same thing in Elixir albeit with more explicit syntax. Giving it the same semantics as the Haskell/JS versions.

"def"
|> (fn(s) -> Foo.concat("abc", s) end).()
|> (fn(s) -> Foo.concat("Alphabet: ", s) end).()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment