 My talk today is called reasonable JavaScript and I know I'm sitting between you and dinner and drinks So I hope you can give me 45 minutes of your time to get through this and hopefully you walk over with something What does that mean for code to be easy to reason about like I've heard it at many conferences I've heard it at meetups. I've heard it I've heard just people randomly say easy to reason about when they talk about some code or some concept like they might say React makes code easy to reason about or one-way data flow makes code easy to reason about or Like static type checking it makes code easy to reason about or you know immutability makes code easy to reason about Pure functions make code easy to reason about and before you know you hear you hear it everywhere in so many different contexts And I didn't really know what it meant and I kind of want to like Create a concrete definition or somewhat of a concrete definition for what this means and why we talk about reasoning about code So that's kind of what my talk is going to be up be about today And so the goals are what makes code easy to reason about so define what makes code easy to reason about Why JavaScript sometimes makes it hard to write code that's easy to reason about and then lastly talk about different Methodologies that we can use to write JavaScript code. That's easy to reason about If you walk away anything in this talk I want you to walk away with knowing what it actually means to reason about code and not hand hand wave over this concept Before getting started someone keep track of how many times I say easy to reason about because it's gonna be a lot Okay, so let's get started I'm gonna start off by saying that JavaScript is a fantastic language for many reasons I mean you're all here today. You probably use JavaScript every day. We're crazy enough to use it on our servers as well So why is JavaScript awesome? Well for one it runs everywhere, right? It's on every browser every operating system every device. It's incredibly everywhere Second it's really versatile like we can use it for a lot of different things We can use it to as a simple scripting language We can use it to build complex web applications. We can use it to build sophisticated apis bots iot apps Desktop apps and now even mobile application native mobile applications. So it's sort of everywhere Immediate feedback loop. I think this is really incredibly important especially for beginners like creators need to see what they're making immediately And I think JavaScript HTML CSS gives you that Most other languages don't give you that as much so That's really important Also the community. I mean you guys are looking you guys are all here today. It's an awesome community I love being part of part of the JavaScript community. That's one of the reasons I fell in love with programming and I and I think the community around JavaScript is just like incredible And lastly, it's beginner friendly and the reason I say it's beginner friendly is because there's no compilation No complex developer environments. All you really need is a text editor and a browser No type system that you have to learn as a beginner Global support in any browser and as I said before immediate feedback loop But I would say this is a blessing and occurs because I think the same characteristics that make JavaScript easy to learn or and great for beginners are also the same thing that make it difficult to write code that's easy to reason about and So I'm gonna start off in an example and kind of show you what I mean by that We're gonna have this calculate area function and you're gonna see this a lot throughout the presentation So I hope you get used to it And all it does is it takes a bunch of radii and it iterates over them and calculates the area and returns the areas of these radii Now as good programmers that we are let's understand the program's behavior Let's actually figure this out if it works right first question. We might ask is does it function? Right like so we might just pass a bunch of radii and check that the output looks correct and to me That looks fine. So we can just say yeah, it functions. It looks good so far The next question we might ask is does it do anything that we don't want it to do like we tested that it works But what if you log the original array of radii we see that it got mutated It's now the array. It's now the area is no longer the radii That's not good. What if we want the radii to calculate the diameter later or the circumference? Whatever it is. So we're mutating our original array Next question we might ask is is it consistent in its behavior? so Here we pass in a bunch of Radii we get an output then some time passes and Later on your program some evil program or checks in some code that updates your pie Variable and then now your radii your areas are totally janky and off. That's not very good, right? Another question you might ask is does it properly handle all possible inputs? So what if radii for some reason ends up being undefined? What ends up happening in JavaScript is you throw it throws an exception a typer and says you can't calculate length and undefined Okay, so then you you being the good programmer that you are you're like, okay, let me go back and fix this Let's redesign our program So step one let's not mutate the original array So what we're gonna do instead is create a new array called the result and Put the areas in that result array and return the result array Another thing we're gonna do is remove the global dependencies so the pie was global and so anyone can mutate it So we move it into our function and put it in the scope of the function so that no one can touch our pie Step three handle invalid input arguments So we're gonna do some error checking before we actually iterate over that Array of radar radii. I'm gonna say if it's undefined or no Do you throw it throw an external error if it's not an array through an error? And then we actually do our logic So now let's go back and understand our improved programs behavior, right? Does it work? Yeah, it works. Does it mutate the original radii? No Does it? Return consistent results over time. Yeah, it seems to and does it handle invalid input? Yeah, it seems to do that too Looking good But I'm gonna do some more battle testing because I thought of another edge case. What if you pass in? undefined instead of a number into an array Because in JavaScript undefined squared is nan we get nan and it silently fails and returns you that array Similarly if you pass in a string You get zero because string squared apparently is zero which is awesome, right? So then you cry and you go back and you redesign your program again And you test for this edge case where you actually check that each of the radii being passed in is a number If it's not there on her otherwise actually calculate the result Now I think we have a winner where it correctly handles all those edge cases So what did we learn? One reliance on global variables causes our programs to be unreliable I think this is a no-brainer. Everyone knows this already, but we just learned that through our calculate area function Second mutable data can lead to unintended side effects because we were able to mutate the radii. We did mutate it We didn't have a restriction around it. So we did Having no way to restrict the type of data that can be passed into a function Makes us think extra hard about error handling because in JavaScript. We don't have types We can pass in anything and so we have to do extra error handling in the function and Type coercion like undefined squared being nan or string squared being zero makes it hard to protect the behavior of our code and realistically if you look at this our Finished version of the program like is this something a beginner would really write like I don't know a year If this was me a year a year ago I probably wouldn't have thought of all these edge cases and written a function like this out of written Just the original one because these are not things. I'm really thinking about I'm just thinking about getting a function to work and so This is why some people get frustrated with JavaScript There's like no compile time restrictions. It allows you to do a lot But you will do anything So one possible conclusion is you can't write code. That's easy to reason about in JavaScript So then we give up Definitely not right. I'm here for the same reason you are and we all love JavaScript So my proposed conclusion is with a little bit of extra effort. You can write reasonable JavaScript So for the rest of this talk, I'm gonna talk about a few of many building blocks that we can use to write reasonable JavaScript And before getting started I want to create a definition for how we can think about thinking about reason me out or like what makes code We easy to reason about so that we can go back after each building block and see which one of these criteria that But building block meets so I'm gonna say When a program does not affect or mutate external state, it's easier reason about and it should not also rely on external state and It should always return the same corresponding output for a given input and we'll see why these three characteristics Leads to code that's easier to understand the intent of easier to reuse easier to maintain Easier to refactor easier to test and hence easier to reason about Okay, let's begin First building block is unit tests So we have our function right original function And we might write a test that says this should work and you pass in an array of radii And you expect it your expected results and you test that it equals your expected result and that passes And if you're a good test writer, you might also write a test This says this should not work and you pass an invalid inputs in this case. You expect that function to throw an error But this test fails it doesn't throw an error because we saw the undefined squared equals Nan and it'll not throw So then you might go back update your program to so that your test pass and now your test pass So if you go back to our bullet three bullet points, how do tests help? Do they help us not affect or mutate external state? Not really Do they help us ensure that we do not rely on external state? Not really But do they help us ensure that they're always returning the same corresponding output for a given input? Yeah, we can test for that because we can always test if you have a test that something should return something We can guarantee that it will always return that So overall why test right? There's a lot of benefits to writing tests. We can verify that our code behaves correctly You can test error cases and edge cases. It's like living documentation essentially and We can make changes without fear because if your tests fail, then you just update your you know that something went wrong So second building block is types Types are not native to JavaScript So I'm going to explain give a definition because I know most people might not be using types yet So a type is just a classification of data which tells a compiler interpreter how the programmer intends to use the data Essentially it just defines a set of inputs and outputs to a function. So I'm going to use a library called flow which was released by Facebook and it's an API that allows you to add types to JavaScript You can also use something like typescript. I chose flow because it's just easier to I find I use it not myself I haven't used typescript, but Sorry, I haven't used. Yeah, I haven't used type script so I'm gonna add types to our original program and You'll see I added a type of pie as number and then I added To our input argument radio. I said that's an array of numbers And then the output is also an array of numbers Now if you had a function that properly passes in an array of radii and when you flow check it It'll say found zero errors But if you had somewhere somewhere in your code if someone passed a string into this function and you type checked it Flow would complain and flow would say string this type is incompatible with array number So it catches this catches this stuff for you at compile time before you ship it to your customers While you're coding so that you can fix it and deploy correct code So why types I want to explain a few different reasons one It separates data from behavior So when you're type when you're writing types It forces you to think about what your data is before you think about what the actual behavior is and this kind Of mental separation helps a lot when you're coding That's our data and then this is our behavior To it serves as limit living documentation. I actually pulled this right out of our code base So we have this function called generate params it takes apparently a quote a commit and a amount and it Return it does some business logic and returns a params object when I looked at this I was like what do I pass to this function? What's quote? Is that a string? Is that a is that a number is that a Boolean like I had no idea how to use this function so and I had to ask around and Then or like grep through my code base and figure out what's being passed into this and then I updated it So that I added documentation on top saying okay. This is this is a Boolean. This is a Boolean. This is a string This is great and all but comments go out a day and documentation goes out of date I know I see so many comments in my code base, which are totally out of date or just terrible documentation It's not consistent. So With types what happens is your your documentation is in your code And anytime you update the function you have and you have to update the type so the documentation automatically gets updated So here we say quote Boolean commit Boolean amount Boolean amount number and then we say it returns an object So another reason is it removes convoluted error handling and testing So this is our flow our type sorry our function with that's type checked, right? So we did we told it that it's gonna take an array of numbers and return an array of numbers Sorry and because of this we don't have to check do all the error handling that we did before Because even if you tried to pass in a string or some invalid input it'll automatically throw at compile time So now our program becomes actually what we would have written without all the error handling Another reason is it eliminates an entire category of bugs so the number one bugs that we see in our react or like front-end code basis typers and type Static type checker would eliminate that so I'm going to show you a very basic example of how this is bought how How a type checker could help so let's say you have some app state and it's There's is fetching it's is fetching or not and then there's an a simple array of messages And then you have a you fetch your data and then you have a component that takes your app state And it just says you have messages dot length on red messages And then it maps over the message and displays the message super simple component But what if your API is unreliable and it returns undefined or no or something Then you have a typer in your bug snag that says typer cannot repropriety of a length of undefined But if you were to use a type checker like flow We can add types We can say our app state is fetching is a Boolean and I'm going to say messages because I know my API is unreliable I'm going to say messages is a maybe an array of strings That maybe means it can either be an array of strings or null and undefined Fets the data the same component now if you try to flow check this well flow will say is Property lent a property cannot be accessed on possibly non null value So it's saying because your message is array can be possibly null you can't do dot length on it handle that programmer So then you go back and you're like, okay, I'll handle that and now you cannot you're reminded to say Okay, if it's is fetching display something else or if the message is null undefined Display a message saying that it failed and if it actually exists then actually display the component that you want it So all this is happening at compile time and the compiler is telling you how to fix your mistakes before it gets shipped So you can define your program in variants and let the compiler tell you when something goes wrong and Then flow will say zero errors and you can do your happy dance Okay, other benefits of types earlier detection of mistakes as we just saw if the the the Satisfaction of like if it compiles it runs Can refactor with greater confidence because if you update a type and you see compiler as you know exactly where the code is being Used incorrectly and it serves as a domain modeling tool Where like you saw a simple example with the app state you can expand that and like actually Model your entire app domain using types, which is really helpful for documentation Okay, so Going back to our checklist. How do types help? Well, do they ensure that we do not affect or mutate external state? Not really Do they ensure that we do not rely on external state? Not really Do they ensure that they always return the same corresponding output for a given input? I'm gonna give this a maybe because they do ensure that they all the function always returns the same type So if you say array of numbers It'll always return an array of numbers, but it doesn't ensure that it always returns the same data per se So I'm gonna give this a maybe But we also we both saw that tests and types give us a lot of benefits And they don't really fit into this like model of these three bullet points So I'm gonna propose that we add another bullet point to our reasoning about what it means to reason about code I'm gonna say when we can when the code is guaranteed to work as intended by the programmer Because that's what tests and types do essentially they guarantee that the the program works as intended by the programmer So this is our updated list Okay, so third building block immutable data So Lee Byron gave an amazing talk about this earlier today So I don't think I have to explain the concept as much but basically Immutable data is when state cannot change after it's created if you want to change the mutable immutable object You don't what you do instead is you create a new object with the change value and point your reference to it So example Here's me today And then I have a me a year from now, and I just increment my age by one And if I check that they're the same it returns truth This is not immutable because we're updating the object But if you were to use an immutable object we can I'm using immutable JS We create an immutable record and then we when we want to update it it creates a new object So we're setting the age to what my age is today plus one and we check that it's equal It's not because it's a new object That's immutable. So going back to our area function. Let's use Immutable data instead of immutable data and Because we're using immutable data. We're forced to map over it and return a new array so here we just Mapping over the input radii and calculating calculating the area And now if you check that the original radii equals the new areas it doesn't so now our data is immutable And probably like okay, so what why would I care about this right? All right, I'm gonna show you try to convince you with another example. Who's gotten this in an interview question. Oh Okay, this makes me cringe So this is quicksort I've gotten it a billion times in an interview question and it's like the most It makes sense logically but the most confusing thing to think to implement Like you start at your new you started at a pivot point and then for each value lower than the pivot You put it you increment the pivot by one and put the value to the left of the current pivot And you keep doing this recursively until you get a sorted array But let's say we were in an interview and we accidentally Make a tiny mistake where you put this J plus plus down here instead of at the top of the if block Then when we go to show the code to the interviewer We get this So then we might put a debugger and try to figure out where stuff is going wrong and then go fix the program to match our intent But we probably won't get the job But what if I told you that with immutable data our function could look like this Each time because we can't mutate the values each time what we do is we first get the pivot Which is the first item in the list then get the rest of the items in the in the array calculate a new Reference to all the items less than the pivot Calculated a reference to all the items greater than the pivot and you're simply at each time concatenate all the items less than the pivot to the pivot and the greater than the pivot to the pivot and Because you're not having to deal with sequence of operations for loops while loops It becomes a lot more declarative like when you read this it looks like you're just describing you What quick sort is you're not actually telling it how to count how to do the quick sort And now when you do the quick sort on this you get the correct answer and so you get the job a lot of people might say wow, it's a lot of objects, especially if you're raised massive and My favorite quote one of my favorite quotes by someone I look up to West Dire is he says make it correct Make it clear and make it concise make it fast in that order like I don't think Unless performance really really matters. I rather have code that looks like declarative and looks like the second example than the first one So overall why me immutable data because when data doesn't change we can just use the data and not think about whether making changes is safe No one can mess with your data nor you theirs So if we go back to our checklist, what does immutable data give us? Does it ensure that we don't affect or mutate external state? Yeah, that's the definition of immutable data Does it ensure that we don't rely on external state not really Does it ensure that it always returns the same corresponding output for a given input not really Does it ensure that it's guaranteed to work as intended by the programmer? I'm gonna say yes because we're not mutating stuff and so there's a higher guarantee of things working as intended Other reasons things like temporal coupling persistence lazy operations. These are things I'm not gonna go into but worth digging into if you're interested about immutable data Okay, so next building block pure functions Definition so I'm gonna start off with what is not a pure function When something relies on some external state that's not explicitly passed to it as an argument So what is a pure function a function that relies only on? Parameters that are explicitly passed to it as arguments to produce a result so example Here here we have a self-executing function I'm gonna pull out and just pass in one I'm gonna say c equals and pass in one and what c is c is a function now that Takes in b and returns a and a is bound to one because I passed in one up there Now if we take c and pass in anything, it'll always return one That's because of the the closure variable The magic of closures right and this is not pure because if you look at the function Be returning a you can't tell just by looking at it What'll return without understanding the magic of the free variable a so this is not pure Similarly, let's say we have some function that just simply tells you if some amount exceeds the by limit of your customer so then you have You could you first fetch the limit for the customer Asynchronously, and then you say if the amount is greater than the limit return true or those false This is not pure either because it relies on some external state simple enough, right? When something relies on external state like this The one thing that becomes really really hard is testing so to test that function We have to now go find this a mocking library mock out the response from the API And then test it which is a pain in the butt we all know But if you were to purify this we're gonna assume this we're gonna assume this function somehow knows the limit And then you can program in like a higher level You can say simply if the amount is greater than limit return true others return false and Now testing becomes so much easier So going back to our checklist Does a pure function ensure that we do not affect or mutate external state? No But does it ensure that we don't rely on external state? Yes Does it always return the same corresponding output for a given input? Yes, because it's never relying on external state So it should always return the same output. Is it guaranteed to work as intended by the programmer? Yes, because a pure function you can look at exactly what it is and you don't need to look at anything external But there there's more When you combine the previous two concepts of pure function and immutability what you get is something called referential transparency and This is simply means the ability to freely replace an expression with its value and not change the behavior of the program So anytime you see a function you can actually replace it by the return value And it'll always be the same and your program should work the same and Why does it matter? Because if you go back to our checklist when you have referential transparency, which means pure functions and immutability what you get is all four So I can end my presentation here But I want to talk about one more building block because I think it's pretty important and it JavaScript is particularly good at letting us use this building block Taking advantage of functions as first-class entities So what does it mean for a function to be a first-class entity, right? It just means we can refer to them as constants and variables. We can pass them as parameters to other functions We can return them as results from other functions So functions you can think of them as variables and pass them around do whatever you want with it So why use them let's take a look I'm going to talk about four different ways that we can use them There's so many different ways that you can use functions as first-class citizens But we won't have time for all of them So suggestion one is replace loops with higher order functions like map filter, etc So an example going back to our original function if we just I'm using a library called ramda. So anytime you see our dot something It's it's a library called ramda, which is a JavaScript utility library for like similar to low-dash, but it's functional and I'm just replacing this for loop with a map and It automatically makes it so much smaller and simpler And if you're like that's not a big deal I'll try to show you next another example and convince you So we have this function some of odd squares less than 10,000 And you need to sum all of the odd squares less than 10,000 So you have an array of odd squares and first you determine what all the odd squares are there less than 10,000 So you have a while loop and find all the odd squares and push them into that array and then you iterate through that array of odd squares and sum them up This is one way of doing it and You get 16 166 650 If you were to use higher order functions, we can break this up like this We can say let's have a simple function that just tells me if x is less than 10,000 let's have another function that tells me if x is odd Let's have another function that tells me if x how to square x and then I'm just gonna I'm just gonna tell it what to do and not how to do it So I'm gonna say first map over my array and square the items Then I'm gonna take the take the ones take all of the ones that are less than 10,000 Filter for the odd ones and sum them and I get the same thing So here I'm telling it what to do whereas before I was telling it how to do it and it becomes so much more declarative So if you go back to our checklist, what are the what are the higher order functions give us? Do they ensure that we don't affect or mutate external state not always but like things like map and filter They'll usually return a new array, but it's not always guaranteed. So I'm gonna give that a maybe Do they ensure that we don't rely on external state? Not really Do they ensure that we always return the same corresponding output for a given input? Not really Do they guarantee that to work as intended by a programmer? Not really But they do give us a lot of other benefits like things are shorter and less clunky. They're easier to read more descriptive Allows us to define computations by what we want instead of how we want it Overall when you use hydro functions, it's more information and less noise Another way to use functions as first-class citizens is parameterizing as much as you can So going back to a calculate area function, let's say later on we want to have a calculate diameter function That's the same thing, but it the the computation in the for loop is a little bit different It just multiplies the radius by two Let's parameterize this right. Let's parameterize their action by extracting out the common code and And Parametizing it so I'm gonna say here's my here's how you calculate an area and here's how you calculate diameter Except my function is wrong Just pretend that this is radius times two for a calculate diameter and then I'm gonna have a generic function that simply takes an action and a list and Maps over that list and performs the action on each item Now I can pass in my calculate area function calculate diameter function with any Into my generic action on list function Now I can use generic functions everywhere. Let's say I can have a square function a double function and negate function and I can pat it can use the same action on list generic function to pass in a The action and any list and so what we end up having is very generic functions instead of Specialized functions to do everything so going back to our checklist does Parametizing stuff ensure that we don't affect our mutate external state not really Does it ensure that we don't rely on external state? Not really Does it ensure that we always return the same corresponding output for a given input? Not really There's a guarantee to work as intended by a programmer not really but it does give us a lot of other benefits like Passing any list to any data and basically having like a generalized function and decoupling the behavior from the data like our action from the list So just in three partial application So first the definition Partial application means we can turn a function that expects multiple parameters into one That will keep returning a new function until it receives all its arguments This is a mouthful and hard to kind of picture until you probably see an example, so I'm going to jump right into an example I have a simple function that applies fees It takes a fee and an amount and it just applies the fee to the amount and returns the returns a new amount This is a regular non partial application function and when you want to use it we have to pass in a fee and an amount if We try to just pass in the fee as in the last example here Here it'll return nan because when you don't pass in all the parameters in JavaScript it'll return nan But if we were to use partial application I'm again using Ramda. I can Partially apply the fee so let's say I have different types of fees like a coin base You have all different types of fees And so I have one function that uses up my apply fees function to create an apply bank fee function And it uses the same function to create a credit card fee function and uses the same function to create a PayPal fee function And now I have these functions that are partially applied and when I want to use them I just pass in the amount. I don't have to worry about passing in the fee I Just pass in the amount and I get the different amounts based on what my fee is So what? All right, so I'm gonna show you another example to see if you could if you might be convinced Here's an old Piece of code that I found in our code base that I updated but this was how it looked Basically what it does is it fetches the valid payment methods for a user and What valid means is first it fetches all the payment methods for the user then it picks off Then it iterates through the payment methods and finds the ones that have allow by true as a as a as a Property and then it iterates through them again and checks that a property limits by the Remaining is greater than zero so meaning their by limit is greater than zero and Then it just formats it by by returning just an ID in the name of the payment Now if you use our own advice and use higher order functions, we can clean it up to look like this We can just filter for the ones that allow by Then filter for the ones where it limit up by that remaining is greater than zero and then just return the ID in the name So that looks way better already. What if I told you that? If we use partial applications, it can look like this Essentially each time you're getting about you're passing in a function that expects data at the very end You're never dealing with the data itself. The data is always coming last Instead you're just dealing with what you want to do with it. You're saying first I want to pick the data property off the response Then I want to filter for the ones where the property allow by is true Then I want to filter for the ones where limit by that remaining is greater than zero Then I want to pick the ID and the name property off that off those objects So in this case because I'm simply telling it what to do. I'm never introducing the data It just becomes so much smaller and cleaner and more expressive So going back to our checklist Does it ensure that does a partial application ensure that we don't affect or mutate external state? Not really. Does it ensure that we don't rely on external state? Not really Does it ensure that we always return the same corresponding output for a given input? Not really Is it guaranteed to work as intended by a programmer? Not really But there's obviously a lot of benefits like you saw number one is readability Number two, it's a powerful way of transforming transforming your data in a functional way Third we can use functions as generic building blocks that work with different kinds of data because you're never dealing with the data the data always comes last and Dependency injection like with the fee example I pass in the fee as a dependency and and I can use I can now use generic functions on With the fee passed in Alright last last building block of premise use composition. This is not this is another way We can use functions as first-class citizens So we saw in all the previous examples how we're kind of throwing functions around We're passing them as parameters returning them as as our return values So once we get used to this idea of passing functions to other functions We can start to discover ways to combine two or more functions together and this is what composition is So start with an example This function just takes an amount and applies tax and shipping and handling and It just applies the tax and shipping and handling and returns you the amount With composition I can break this up into apply tax and then apply shipping and handling There are two separate functions and I compose them So I'm doing ramda that compose and how compose works is it works backwards from left to right So first you'll apply tax the data I passed in then I'll apply shipping and handling to the data I passed in Let's say later We want to add a gratefulness surcharge surcharge because I live in San Francisco and people find reasons to Tax you on the most ridiculous things and surcharge you on the most ridiculous things like I got a cleanliness tax once At a restaurant, which is awesome So let's say we have an apply tax and shipping and handling and gratefulness surcharge function So we might just add our great gratefulness surcharge and get back that Or if you were to use composition We can create a simple apply gratefulness surcharge function and just pop it on to our compose stack Like that and then later. Let's say we don't want to add this surcharge We can also create a separate function that doesn't apply that charge Later, we want to add a 5% discount So we can either apply tax and shipping and handling and gratefulness surcharge and discount Or we can use composition and apply our discount and pop it on to our compose stack I think you're starting to get the point, but in case you don't let's say we want to also format money I'm not even going to I'm not even going to go into the regular example I'm just going to go into the composition example But you just create a function that formats money and then pop it on to our compose stack and We get our formatted amount. So why composition? Each function becomes useful on its own We can easily create fresh new functionality from existing functions We can break apart pieces of behavior and test them independently We can test each of those functions independently and easily because there's so much more simpler and A declutter code so readability again So going to our checklist does composition ensure that we don't affect or mutate external state not really Does it ensure we don't rely on external state? Not really Does it ensure that we always return the same corresponding output for a given input not really? Does it ensure that we're guaranteed to work? That does it is does it ensure that it's guaranteed to work as intended by a programmer? Not really So we saw that like with first-class functions. It's a little bit weird They didn't really meet any of our criteria for reasoning about code necessarily so But they did give us a lot of advantages, right? Like when you start to take advantage of advantage of functions as first-class innocence It encourages to use functions as the primary unit of abstraction Like small independent composable functions become the tool we used to model and solve any problem And we saw all the benefits of doing that So I'm going to propose that we add one last bullet to our list of what it means to reason about code I'm going to say and code is modular and generic Enables us to read them reason about code better And that's what functions as first-class citizens gives us it creates modular and generic code So when you have all these What you get is code that's easier to understand the intent of easier to reuse easier to maintain easier to refactor easier to test and easier to reason about So recap we talked about test types Immutable data pure functions functions as first-class entities and and within that we talked about how to higher order functions over loops Parametizing as much as you can composition and partial application So I'm not gonna leave without putting it all together and in one of course So here I'm gonna go back to our calculate area function and use basically everything I just talked about you don't have to go this extreme obviously. I'm not saying you have to adopt every one of these things I'm just showing you what it looks like if you want it to I'm importing a list from immutable.js and map and pipe from Ramda. I'm using flow as you can see So I have cons my pie Defined as a number Then I'm creating a type of action and this type of action is takes in a value Which is of type number and returns a number and I'm creating a generic Sorry, I'm creating a calculate area function that is of type action that takes radius Which is a number and returns the area which is a number which is what I define the action type to be and Then I have a generic do stuff on list function that takes a list which is of type list number Which is a list of numbers And it takes an action which is of type action that I defined up there and then returns a list of numbers I'm just mapping over that list and performing the action on it and then I have my Radi I which is an immutable list and then I do stuff on the list I pass in the radii and the calculate area function and I get my list. I haven't mutated anything There are a couple of things I missed in here our composition, so I want to put that in here, too So I have another function format, which is also of type action similar to Our calculate area action it takes a radius and it formats it so it truncates all the decibels extra decimals and I'm gonna Pipe pipe is opposite of composed, so it just does Right to left left to right, so I'm gonna first map over my array and calculate the area Then map over it and format it and then what I end up getting is a formatted list So everything in one So I don't think we need to go to this extreme of obviously using everything I talked about I think the point of it is that you can kind of pick and choose in JavaScript what you want to use like if Type errors are a huge thing in your code base. Maybe introduce static type checking That might help or if you're finding that it's really hard to maintain large state Maybe using immutable data might help like Lee Byron said or if you're finding that you are having a hard time Testing functions. Maybe think about using more pure functions like Figure out what your problem is and solve for that You don't need to use everything but what's amazing about JavaScript is we have all these tools that we can use to write better JavaScript So putting it all together JavaScript strives to be an incredibly dynamic and flexible language This sometimes puts the burden on a programmer to consciously write good code and It makes it easy for someone to bash on JavaScript for its dynamic typing aggressive type corrosion mutable data structures And whatever you want to bash on JavaScript for But just remember that JavaScript wouldn't be where it is today if it weren't this flexible and if it weren't this dynamic It's incredible flexibility and forgiving nature is exactly what gets beginners in the door and enables it to be used for everything that it is used for and We're fortunate enough that there are tools and methodologies that enable us to write good JavaScript code So I call this a win-win situation So let's go write some code. That's easy to reason about