{{ message }}

Instantly share code, notes, and snippets.

# Avaq/placeholder.md

Last active Apr 15, 2019
Exploring an alternative placeholder implementation

I published a library which implements this idea; furry.

# From To Normal Alternative
1 `a → b → c` `a → c` `f(_, b)(a)` `f(_, b)(a)`
2 `a → b → c` `b → a → c` nope `f(_)(b, a)`
3 `a → b → c → d` `b → d` `f(a, _, c)(b)` `f(a, _, c)(b)`
4 `a → b → c → d` `a → b → d` `f(_, _, c)(a, b)` `f(_, _, c)(a, b)`
5 `a → b → c → d` `a → c → d` `f(_, b)(a, c)` `f(_, b, _)(a, c)`
6 `a → b → c → d` `b → a → d` nope `f(_, _, c, _)(b, a)`
7 `a → b → c → d` `c → a → d` nope `f(_, b)(c, a)`
8 `a → b → c → d` `c → b → d` nope `f(a, _)(c, b)`
9 `a → b → c → d` `b → c → a → d` nope `f(_)(b, c, a)`
10 `a → b → c → d` `c → a → b → d` nope `f(_, _)(c, a, b)`
11 `a → b → c → d` `c → b → a → d` nope nope
12 `a → b → c → d` `b → a → c → d` nope nope

The alternative has another advantage: You can supply the placeholders in curried fashion, something which is impossible with the normal semantics:

``````f(a, b, c) === f(_, _, c, _)(b, a) === f(_)(_)(c)(_)(b)(a) === f(_, _, c, _, b, a)
``````

### Notes

• Any case where the last argument is the placeholder (like `2` and `8`), is equivalent to applying `flip` to the function without providing the placeholder.
• The alternative method almost provides the full power of `rearg`, except for cases like `11` and `12`. These kinds of cases would become more common as the arity of the function increases.
• The alternative method has exactly the same syntax for the majority of use-cases. Note that only `5` differs. These differences will become more abundant as the arity of the function increases.
• The alternative method has more consistent behavior; there's no difference between calling your function `f(_)(_)(_)` or `f(_, _, _)`.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 console.clear(); const _ = {'@@functional/placeholder': true}; const is_ = x => x && x['@@functional/placeholder'] === true //Rotates a function so that its first argument becomes its last. const rotate = f => (...xs) => (xs.unshift(xs.pop()), f(...xs)); //Partially apply a function with an array of arguments. const partial = (f, xs) => (...ys) => f(...[...xs, ...ys]); //Curry a function based on its reported arity. const curry = f => curryN(f.length, f); //Curry a function using the given arity. const curryN = (n, f) => (...xs) => { const o = xs.reduce( (o, x, i) => ( o.n === 0 ? (o) : is_(x) ? ({f: rotate(partial(o.f, o.xs)), n: o.n, xs: []}) : o.n === 1 ? ({n: 0, f: o.f(...[...o.xs, x]), xs: []}) : i === xs.length - 1 ? ({f: partial(o.f, [...o.xs, x]), n: o.n - 1, xs: []}) : ({f: o.f, n: o.n - 1, xs: [...o.xs, x]}) ), {f, n, xs: []} ); return o.n === 0 ? o.f : curryN(o.n, o.f); } const test = curry((a, b, c, d) => a + b + c + d); ([ test('a', _, 'c', 'd', 'b'), test('a', _, 'c', _, 'b', 'd'), test(_, _, _, _)('a', 'b')('c', 'd'), test(_, _, _, _, _, 'b', 'c', 'd', 'a'), test(_)(_)(_)(_)('a')('b')('c')('d') ])
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
 console.clear(); const _ = {'@@functional/placeholder': true}; const is_ = x => x && x['@@functional/placeholder'] === true //Changes a curried function of arity n so that its first argument becomes its last const rotate = (n, f) => { if(n < 2) return f; const next = xs => x => xs.length === n-1 ? xs.reduce((f, x) => f(x), f(x)) : next([...xs, x]); return next([]); } //Changes a curried function of arity n so it may accept multiple arguments at once const recurry = (n, f) => (...xs) => { if(xs.length < 1) return recurry(n, f); const r = xs.reduce( ({n, f}, x) => is_(x) ? {n, f: rotate(n, f)} : {n: n - 1, f: f(x)}, {n, f} ); return r.n < 1 ? r.f : recurry(r.n, r.f); } const test = recurry(4, a => b => c => d => a + b + c + d); ([ test('a', _, 'c', 'd', 'b'), test('a', _, 'c', _, 'b', 'd'), test(_, _, _, _)('a', 'b')('c', 'd'), test(_, _, _, _, _, 'b', 'c', 'd', 'a'), test(_)(_)(_)(_)('a')('b')('c')('d') ])