This is not so much about nested functions but about schönfinkeling / currying. Schönfinkeling / currying is named after Moses Schönfinkel who developed this technique (after Gottlob Frege had previously introduced it) and Haskell Curry, who perfected and described it.
In simple terms, currying is a technique that allows to turn any function of n arguments into a function of n-1 arguments which returns a function that takes the nth argument. And by applying this repeatedly, you can show that you never need functions of more than one argument, to model functions with arbitrary many arguments.
Here is an example. I can turn a function that adds two numbers:
function add(a, b) { return a + b; }
add(2, 3)
//=> 5
Into an "adder factory" that returns adder functions, which when called will then produce the sum of the two numbers:
function adderFactory(a) {
return function adder(b) { return a + b; };
}
const twoAdder = adderFactory(2);
twoAdder(3)
//=> 5
or
adderFactory(2)(3)
//=> 5
Now, you might think: "but ECMAScript supports functions with more than one argument, so why would I simulate them using currying, if I can have them natively?" And you would be right! It makes no sense to use currying for this reason.
But, there is another interesting thing that you might want to do with functions: partial application. "Function application" is just functional programming speak for "calling a function", so "partial application" means "calling a function with only a subset of its arguments". Partial application calls a function with only some of its arguments, and produces a function that is specialized for only those arguments. In a language that supports partial application, I could do something like this:
const fourAdder = add(4, ?);
But, ECMAScript doesn't have partial application.
However, when I curry my functions, then I can do "sort-of partial application", where I can at least only supply the first few arguments, and leave out the last few arguments. This means that you have to think about which arguments are more likely to be fixed and which arguments are more likely to be variable, and you should order them by "variability".
So, in the case of the function that you posted, it is possible to create a base converter that can only convert one specific number from one specific base into any number of bases. I must admit, that is actually not terribly useful. It would be much more useful if the function were defined like this:
const convertFromBaseToBase = baseFrom =>
baseTo =>
num => parseInt(num, baseFrom).toString(baseTo);
convertFromBaseToBase(2)(8)('1001')
//=> '11'
Now, you can, for example, create a converter from octal to hexadecimal like this:
const octalToHexadecimalConverter = convertFromBaseToBase(8)(16);
octalToHexadecimalConverter('17')
//=> "F"
Note! With the restriction that you can only "partially apply from the right", you could actually also do this with optional parameters with default arguments, kind of like this:
const baseToToken = Symbol('baseTo'),
numToken = Symbol('num');
function convertFromBaseToBase(baseFrom, baseTo=baseToToken, num=numToken) {
if (num === numToken) {
if (baseTo === baseToToken) {
return (baseTo, num=numToken) =>
num === numToken ?
num => parseInt(num, baseFrom).toString(baseTo) :
parseInt(num, baseFrom).toString(baseTo);
} else {
return num => parseInt(num, baseFrom).toString(baseTo);
}
} else {
return parseInt(num, baseFrom).toString(baseTo);
}
}
convertFromBaseToBase(8, 16, '17')
//=> 'F'
convertFromBaseToBase(8, 16)('17')
//=> 'F'
convertFromBaseToBase(8)(16)('17')
//=> 'F'
convertFromBaseToBase(8)(16, '17')
//=> 'F'
But, as you can see, this starts to get real ugly, real fast.
The snippet in the question also is useful for another reason: it provides a fluent interface which gives names to the particular parameters, so that you can not confuse the two number parameters baseFrom
and baseTo
. This can, however, also be solved in a couple of other ways. One is by naming the function such that it is clear whether the baseFrom
or the baseTo
comes first, i.e. instead of convertBase(num, baseFrom, baseTo)
call it convertNumberFromBaseToBase(num, baseFrom, baseTo)
. Another possibility would be to use an object parameter, like this:
function convertBase({ num, baseFrom, baseTo }) {
return parseInt(num, baseFrom).toString(baseTo);
}
convertBase({ num: '17', baseFrom: 8, baseTo: 16 })
//=> 'F'
But, even when using a more descriptive function name or a fluent interface, then it would still make sense to change the order of parameters, to make currying and partial application more useful.
Note also that I haven't said anything at all about nested functions that are not used for currying, as in this case, for example [Code adapted from Ruby Recursive Indexing/Searching Method (Using Middle Comparison) Returning Incorrect Index Value]:
function bsearch(arr, target) {
function bsearchRec(arr, target, offset=0) {
const middleIndex = Math.floor(arr.length / 2);
if (arr[middleIndex] === target) { return offset + middleIndex; }
if (arr.length === 1) { return undefined; }
if (target > arr[middleIndex]) {
return bsearchRec(arr.slice(middleIndex+1), target, offset + middleIndex + 1);
} else if (target < arr[middleIndex]) {
return bsearchRec(arr.slice(0, middleIndex), target, offset);
}
}
return bsearchRec(arr, target);
}
bsearch([1, 3, 4, 5, 9], 5)
//=> 3
Here, the nested function bsearchRec
is nested inside bsearch
because it is a private internal implementation detail of bsearch
, and nobody except the author of bsearch
should know about it.
And lastly functions are the vehicle used in ECMAScript for encapsulation. In particular, functions are how ECMAScript implements objects. Objects have behavior identified by names, and encapsulation. In most OO languages, those three things, the behavior, the encapsulation, and the mapping of names to behavior (aka "method calls") are provided by one entity, the object. In ECMAScript, encapsulation is provided by functions (closures), behavior is provided by functions (nested inside closures to share private state), and the mapping from names to behavior is provided by dictionaries, which are confusingly called objects, even though they only implement one third of what it means to be an object.
So, without nested functions, there would be no encapsulation in ECMAScript, and most importantly, no objects! Even modules and classes are mainly syntactic sugar on top of nested functions.