This article is about how I rewrote my library in a functional programming style. is a small 2d library that models and as . This article math and linear algebra though — you don’t have to know that much to be able to get some value out of it. vec-la vec-la linear algebra vectors matrices plain javascript arrays is not about Crash course: What is a Vector? In case you don’t know, a vector is basically an ordered collection of numbers. A vector is just an ordered collection of 2 numbers. If you’re familiar with coordinates like (2, 3) then you know what a vector is intuitively. 2D Vectors can be visualised as arrows on the plane But a vector can also be seen as an arrow in space; It has a direction (where the arrow points to) and a magnitude (how long the arrow is). That’s important because it means that a vector can represent more than just points in space — it can just as well represent speed in a direction for instance. In many ways, vectors are like normal numbers. You can add, subtract, and scale them, but you can do other cool stuff like rotate and reflect them. That’s what is all about — it gives you the tools to manipulate these mathematical objects programmatically. vec-la I primarily use to build and games, although the use cases for vectors and matrices are wide. vec-la programatic animations Goal With all of that out of the way, the primary goal of both the original project and the refactor was to move away from the approach that many other libraries take; Class based vector models where operations are that mutate the vector. methods Avoiding mutation of state is an idea that has become more and more widespread in the last few years. Redux in particular has brought this into the spotlight in the javascript community. Instead, vec-la uses that always return a new copy. To illustrate these differences though, let me compare to another (much more popular) library, . pure functions victor.js In the normal case Victor requires creating a specialised object, but it also exposes an API for creating from an array: Victor.fromArray([15, 22]) Although actually ends up being more tedious than creating vectors with new. Using , you can see the idea of coming in. The result of adding to is actually stored in . This is annoying if you want to continue using afterwards. will always return a new vector, and so you never have the mental burden of worrying about what happens to and . add mutation vic1 vic2 vic1 vic1 vec-la vec1 vec2 Same here for multiply in — it mutates the vector your working with. This can often lead to unexpected and difficult to find bugs. To see why take the following code: victor Victor also does not allow for some other more advanced functionality that does, like using matrices. If you don’t know what a matrix is, here is a simple but inexact explanation: vec-la A matrix is a set of numbers arranged into a table. There is a special way to combine these numbers together with a vector or matrix that gives you a new vector or matrix. This operation is called Matrix multiplication. I find it helpful to think of a matrix as being the definition for a kind of transformation function. Writing matrices by hand is cumbersome and difficult to read, so there is an API using a to simplify creation: fluent builder pattern A functional level up You might already say that is pretty functional. vec-la It works with functions, not methods It never mutates arguments Functions that take or return vectors plug easily together — they compose But it has some drawbacks too: The functions are not curried They often take arguments in the “wrong” order The introduces a specialised object into the mix which is not a plain array matrixBuilder If you don’t know about currying and argument order, check out my article I go deep into detail about these concepts there. Making Functional Programming Click . Let’s see how to fix each of these drawbacks. The functions are not curried That’s a fairly easy fix, but it actually needs to be combined with fixing the argument order. As I stated in the article I just mentioned: The rule is typically put the data as the last argument Most of the time that data is going to be the vector you want to operate on. Let’s use the function to see the problem: scale This function takes a vector and a scale factor as arguments. Because of that order, you cannot partially apply this function in a useful way; It’s much more likely that you want to scale vectors by the same factor, than it is that you want to scale the by scaling factors. If you swap the arguments, you can make a more meaningful function. v sc different scale same vector different There are a few more functions like this in the library. takes a vector and a matrix and transforms the vector according to the matrix. Let’s imagine that you have a shape defined by an array of vectors. If you want to transform every point, you could do this: transform If we switch the arguments we can create a partially applied transform function that will always use our matrix: That’s a lot more expressive and reusable. If you have another shape or point you can transform it using that same function. Before we were forced to create an arrow function that implicitly has access to the matrix. As a last example (there are plenty more), there is a convenience function called , which takes a vector to rotate, a vector to use as the rotation origin (control point), and an angle. If you want to rotate a bunch of vectors around the same point by the same angle, a better order would be angle, rotation origin, vector. rotatePointAround There’s a good possibility that you’re thinking of counter examples and exceptions, because they certainly exist. In those cases, we can use something called a combinator to rearrange the arguments on the fly. A combinator is any function that takes a function as input and produces a function as output. Let’s imagine that we have a vector, and an array of matrices. We want to produce an array of vectors where the one vector is transformed by all of our different matrices. In this case, we want to flip the arguments of transform. Instead of writing a special custom function, we can write a generic combinator to achieve this: It’s a less common use case, but having a function like makes it easy to manage when it does come up. Like , you’ll find an implementation of in all the major functional libraries like ramda or sanctuary. flip2 curry flip The matrixBuilder() problem We’ve solved the first two problems, and kept the possibility of runtime argument rearrangement open if we need to handle unusual cases. But there is still the problem of the matrix builder. All of the functions in the new library have been built to support function composition, but a fluent interface simply doesn’t; You have to keep calling methods until the matrix is ready, and then call to release the matrix value. (if you don’t know what function composition is, read the article I mentioned before) get() Let’s take a look at the original matrixBuilder code. The function takes a matrix (or nothing, which implies the ) and returns an object with some methods for rotate, scale etc. From each of those methods, it returns a new matrixBuilder which composes the old matrix and a new one that performs a given operation. vMatrixBuilder identity matrix That word compose should be jumping off the page at you right now. It turns out there is a way to take two matrices and perform a complicated sum of the numbers inside, and get a new one that encodes the information of both. We call that operation matrix composition, and the crazy thing is that this works exactly the same function composition (of course it’s not really crazy — math tends to be a well thought out process of generalisation, so these types overlap happen a lot). That’s probably the most intimidating piece of code in this whole article. I showed the function just to show that there is some kind of way to compose matrices, but understanding is not important (and is essentially impossible just from this implementation). . mCompose If you want to know how all the math for this really works, I recommend 3blue1browns’s series on “The essence of linear algebra” The rest just works exactly the same as — it creates matrices by partially applying to a specialised matrix. And just like you need to kick the whole thing off with a matrix, which is typically in both cases the identity matrix. The identity matrix, unsurprisingly, is just like the function in programming — when you apply it to a matrix or vector you just get the same thing you put in. matrixBuilder mCompose matrixBuilder, identity Now we can express a build up of matrices through pure function composition, and we’re not restricted to the kind of methods that one particular API exposes. Summary Reframing the library this way leads to a much higher interoperability with other libraries — especially functional ones like ramda, which I like to work with. To see an example of all of this coming together, check out this programatic animation on codepen. I will admit that the matrix builder is an interface conceptually, but it trades off and to achieve that interface. I think both libraries and approaches have their place. easier flexibility generality You can see both the new , the original on github. They are both around 150 lines of actual code, so it’s a pretty easy project to get your head around. vec-la-fp vec -la If you’ve enjoyed this article, let me know by leaving me a comment here or on . twitter I’m starting a series called “exploring ramda” where I’ll be writing about incorporating a functional style in javascript using the ramda library, so follow me here on medium if that’s something that interests you.