Projection functions such as id
(1P1) and const
(2P1) are very useful in functional programming. However, sometimes you need more complex projection functions such as 5P4. You could write it manually in pointful style as a => b => c => d => e => d
but this is tedious and not very readable. Hence, it would be nicer to be able to write proj(5)(4)
instead. Here's how I currently defined this generalized projection function in JavaScript:
const proj = n => m => curry(n, (...args) => args[m - 1]);
const curry = (n, f, args = []) => n > 0 ?
x => curry(n - 1, f, args.concat([x])) :
f(...args);
console.log(proj(5)(4)("a")("b")("c")("d")("e")); // prints "d"
As you can see, this implementation of proj
is a hack because it makes use of variadic arguments. Hence, it can't be directly translated into languages like Agda or Idris (where the generalized projection function is actually typeable due to dependent types). So, how to recursively define a generalized projection function in such a way that it can be directly translated it into Agda?
All projection functions can be decomposed into the following three combinators:
Using these three combinators we can define the various projection functions as follows:
As you can see, there's a recursive pattern here:
Note that
K = CI
which is whyproj 2 1
works. Converting this directly to JavaScript:I'll leave it as an exercise for you to figure out the dependent type of this function.