This blog series I will cover the how and why functional in js. This part is about how to compose functions. The next parts will cover Type Classes, fmap, map, fold, monoids, functors, moands and more goodies.
I will not cover higher order function in this series. If you do not know what they are or how they work take a look at this post. Nor will I cover basic array manipulation like map, reduce and filter.
The first thing to understand functional programming is to understand types. When I say types I don’t mean the traditional types of C static languages. I mean a inferred type system like Haskell. The types make easier to understand the functions and analyzes them. (For a more detaild explonation).
We want typechecking so we don’t accidentally try to shove a square peg into a round hole and create bugs in the process.
I will be using es6 arrow functions in this article to make the code examples less verbose. Use the Traceur repl to make the examples work in your browser / node.js. If not your from the future and ES6 have allready been released, then ignore this message.
I will write the syntax of the type annotations in haskell style. Inside a special form of js comment that begins with a “+” in the code. Like:
List will be written like:
And type annotations for function will be:
Where the last type is the return type of the functions. And the first to the next to last types are the parameter types of the function.
Why write the type annotations like this? Because of function application!
Type variables (Called generics in some languages).
Type variables can be of any valid type. As long the same variable (T in this example) is of same type everywhere in the type expression.
When we talk about functions in a functional manner we usually mean pure functions. A pure function is a function that exactly produces the same output over and over again if you feed it the same input value.
A pure function can be written as a table with the input and output values of the function. In matematics a function is a mapping between the input set and the output set of a function. This view of a function greatly simplifies the complexity. And the biggest bonus is that the functions are side effect free! The functions are easier to test, and easier to debug when they missbehave. This is because of they are side effect free, so you don’t have to look outside the function for side effecting logic.
Another posibility if you have pure functions is memoization. It is the process where you turn a function into a table dynamicly. Where you cache function values inside some cache object. And becouse the functions can’t change their values for the input parameters. You don’t have to worry about cacheing errors.
The canonical example is fibonacci.
Try to run fib(35). It takes forever to finish.
This version finishes almost instantently if you run memoFib(35). This is becouse the memoized version does’t have to recompute every value for each recursive step. So it takes almost linerar time to run this.
Another benefits to pure functions are automatic parallelization. If you know your functions can’t have any side effect you can run them on diffrent cores / cpus. A good example of this is Intel’s River trail project (a demo video). They use pure functions in their api.
Function application is when you take a function and partial applies its arguments with some values. But what does this even mean?!?
First take a chill-pill, and lets look at an example.
We have defined an add function above. Now we want to make a counter function that increments the value by something.
We apply the currying value to the first argument of the add function. The remaining arguments can be feed through the function wrapper around the function call. So we can easily create arbitrary many counter functions from the add function.
So the answer to why do this, is code reuse and higher level of abstraction of the code.
But curry2 only works on functions with two parameters. What about currying any function?
But where is the type definition of the curry function? The answer is it can’t written as a concrete type because the curry function’s arity is variadic. Usually we don’t want to write functions that are variadic. But in some cases it can’t be avoided.
The real use of function application comes when you combine it with function composition.
There are also right currying which applies the values from the right. And partial application which you can apply function with values at arbitrary positions.
where “__“ is a some “magic object” value the partial function scans after and ignores that value when it applies the arguments. Then the returned functions values replaces the “__“ values in order with it’s arguments.
Function composition is when we take the output of a function g and shove it into a function f. This is the part when type safety is a big win. Because it guarantees that the functions types fits together.
Function composition is THE WAY to control complexity. It is the way complexity is controlled in mathematics. Small function (or logical rules, but it is the same thing) that you can compose to create a grant symphony of controllable complexity.
The only requirement is that the output type of g must be the same as the input type of f.
But you can’t do as the following example. I precent to you the batman function:
If we had a typechecker we would have cought it.
Usefull code from this article