JavaScript Jems - Functional And Not Quite Functional |
Written by Mike James | |||
Monday, 07 December 2020 | |||
Page 2 of 2
Functional Programming in JavaScriptThere are libraries that make it easier to program in a functional style in JavaScript, but the language already has enough facilities for a reasonable approach to functional programming. The most obvious functional programming aspect of JavaScript is that it has first class functions which makes functional composition and higher order functions trivial. This is not the place to go over these ideas as they have been covered in other jems, but there are plenty of examples in what follows. Perhaps the biggest problems with functional JavaScript is that side effects are the norm, unless you go to extreme lengths, and mutability is the default. JavaScript may not be a functional programming language but it does have first class functions and this makes it very easy to create and use higher order functions. It is also important to point out that the lambda or arrow functions introduced in Jem 11 have very little to do with functional programming. That is JavaScript isn’t more functional because it now has lambda functions. It is true that the lambda calculus influenced functional programming and lambda functions as found in many languages refer to this, but they are simply syntactic sugar and anything you can do with an arrow function you can do with a standard function. They are useful but they aren't "deep". JavaScript isn’t a functional language but it does have some features which lend themselves to a sort of functional programming - perhaps function-oriented programming, FOP, is a better name and we’ll return to it in Jem18. Immutable data and iterators are discussed in other jems, here we will look at higher order functions.
Higher Order FunctionsAs already stated, more than once, JavaScript has first class functions and this is a jem. As they are first class functions they can be higher order functions, which simply means they accept functions as parameters or return functions as a result. If you have programmed in JavaScript at all you will almost certainly have encountered higher order functions in the context of callbacks. Any function that asks you to supply a callback function to use when it has finished is by definition a higher order function. However, as explained in Jem 14, higher order functions are for more than just callbacks. Higher order functions are everywhere in JavaScript, but we tend not to think of using the same ideas with our own functions. For example, if you wanted to time how long a function takes to execute you could write a decorator to modify any function: function timeFunction(f) { return function (...args) { var t1 = performance.now(); var result = f(...args); t1 = performance.now() - t1; console.log(t1); return result; }; }
The performance.now method gives millisecond accurate timing, but you can use the now method as an alternative. The important general points are that the arguments are passed using the rest parameters operator, which creates args as an array out of however many parameters are passed, and the destructuring operator is used to pass as many parameters as were supplied. Confusingly both these operators are … that is three dots. Notice that this allows the creation of a higher function that can accept a function that takes any number of parameters and call it without having to know the number of parameters in advance. For example, you can create a function with three parameters: function sum(a, b, c) { return a + b + c; } and pass it to another function timeFunction to add the timing instructions: sum= timeFunction(sum); console.log(sum(1, 2, 3)); Notice that the change to the function is permanent. If you want a temporary change then assign the modified function to a new variable. Perhaps the best known and most used higher order functions are map, filter and reduce. These accept a function as a parameter and apply the function to every element of a collection. So impressive are these higher order functions in making code more compact, and more importantly understandable, that they are often the reason for an initial interest in functional programming. They are perhaps the main reason some programmers adopt FOP without going the whole way to functional programming. More of these higher order functions in Jem 18: Functional Approach to Iteration. Omitted from this extract:
Functional Composition and Fluent StyleAt its simplest, functional composition just means calling a function to operate on the result of an earlier function. If you have function F and function G then you can compose them as: F(G(p)); Simple, but notice that this only works if G returns an object which F is expecting to work with. If F is expecting a number and G returns a non-numeric object then clearly the composition will not work. Also notice that although the composition is written F followed by G it is G that is evaluated first and then F. The way that the output of one function becomes the input to another lets us think in terms of function "pipelines" and this in turn suggests fluent interfaces or chaining. Currying takes us one step towards a fluent interface. If function F returns function G then we can write: F()(p) and things are happening in the order written. Take this one stage further, and have function F return an object with function G as a method and we can write: F.G(p) and so on,.. and fluent style is born. For a bigger example, consider the DSL for arithmetic introduced in Jem 7. The functions involved - sqrt, square, times and display - could be implemented as functions that return a numeric value and a calculation could be written as: display(times(3,display(square(display(sqrt(100)))))); As you can see, the problem is that the nested brackets make it difficult to get right. If instead of functional composition we allow each function to return an object which has the necessary functions as methods, then this can be written as: var c = new CalcConstruct(); c.setValue(100) .sqrt() .display() .square() .display() .times(3) .display(); See Jem 12: Fluent Interfaces for more detail. Arguably fluent interfaces or chaining make functional composition easier to work with. In particular, notice that the order in which the functions are carried out is the same as they are written. Fluent style is one of the things that attracts beginners to the use of functions, even if it isn't the full functional approach. So JavaScript's first class functions are a jem, but we already know this. What makes them even better is that the way that the arguments object, rest, spread and bind all make it possible to really work with functions in higher order functions. The way that it fits together is indeed a jem.
Now available as a book from your local Amazon.JavaScript Jems:
|
|||
Last Updated ( Monday, 07 December 2020 ) |