{{ 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(_, _, _)`.
 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') ])
 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') ])