Adam Wathan - Curing the common loop - Laracon EU 2016

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
everybody you guys hear me good cool yeah so Sean said my name is Adam Wathan no real time for pleasantries we got a lot of code to get through so today I want to talk to you guys about curing the common loop so has anyone ever written code that looks like this before or at least worked with code that looks like this before and hated working with code that looks like this before so the goal of this presentation is to teach you how to never have to write code that looks like this ever again for the rest of your life so to do that we need to talk about some fundamentals so I want to talk to you guys a little bit about higher-order functions so a higher-order function is a function that either takes a function as a parameter or returns a function or even does both so there's something that you work with very often and laravel for example when you're defining a route like a closure based route the get method on the router is a higher-order function because it accepts a function as a parameter or in your sending mail so the send method or the cue method on the mailer class is also a high order function because it takes a function as its third argument or a database transaction you know it takes a function as an argument and that's the code that you want to run within the transaction so they're very useful now they're the sort of thing that you work with elan and you maybe use a lot in other people's api's like a lot ilan and other people's api is like especially if you're writing lots of JavaScript you know callbacks everywhere constantly all the time but there's something that took me a couple years to really fully start to appreciate the power of when designing my own API so I'm writing my own code versus just kind of using them in other people's code and sort of thinking I knew what the benefits were so the reason that higher-order functions are powerful is because they afford more powerful abstractions stuff that you can't do when you're just working with values as parameters versus bits of code that you can pass around so for example here's a snippet of code that takes a list of customers and converts that list of customers into a list of their email addresses has anyone seen this pattern in their code before where you start with an empty array loop over something else stick stuff in that empty array and then eventually return the new array that you built up yeah a few people so here's another piece of code where we are trying to determine maybe we're trying to build a rapport around figuring out what the value we have on hand is of different products in our store so you can see we're looping over inventory items and each item has like a product name it has a quantity like how much we have on stock or in stock and it also has a price we're putting together this little data structure that kind of tells you what the product name is and the total value that we have on hand of that product right now if you look at this piece of code the only thing really unique about it is this part here where we're extracting the email from the customer right and if you look at this piece of code it's kind of the same thing the only thing really unique about it is this stock total data structure that we're putting together if you take the rest of that code like the stock totals the inventory items and the stock total object and you generalize the names of everything you end up with something like this where you have an empty results array and you're iterating over items and you do something with that item to create a result and sticking it in that results and ultimately returning these results we can do the same thing with the other piece of code that we had and now we have two pieces of code that are more or less identical right like the only thing that's different is this little grayed out area other than that everything is the same so how can we generalize this how can we create an abstraction around this well normally if you have two pieces of code and in those two blocks of code there's maybe some code that's the same what you would normally do is like extract that out into a function right to avoid that duplication so you have some function that centralizes that bit of logic and you use that function in two places so you only have to update it in one place like normal named functions are really good for extracting duplicated pieces of code in this case we're kind of working in the opposite situation we have two pieces of code that are almost entirely the same and we want to extract out one piece that's different and anonymous functions are really good at doing that so if we take this little piece of code here we're extracting the email from the item and we extract that out into a callback that takes the item and returns the email associated with that item and replace the code underneath with just executing that callback and passing in that item we can do that exact same thing with this example and now we have two pieces of code at the bottom that are exactly the same and we've parameterize to the difference to those two pieces of code if we take this piece of code and wrap it in a function we've essentially implemented a remap from first principles so now if we take this loop code here where we're trying to extract those customer emails instead of creating this initial empty array and building it up by stuff in the items emails into each one we can replace that with just a call to a ray map that just takes that array of customers and maps it into array of that customers emails we can do the same thing with a stock totals example we just take this whole loop and replace the entire thing with a call to a ray map where we return that data structure another one has anyone ever seen code that looks like this before like this general kind of pattern where you start with an empty array you iterate over some items you check some condition and if some item satisfies that condition it gets put in the new array but if it doesn't it gets left out of the new array and then you've returned that new array at the end seem kind of familiar so in this case we were trying to figure out what products are out of stock so we start with an empty out of stock for all X array loop over our products check to see if that product is out of stock then when you stick it in the out of stock products array if not we just ignore it and then ultimately at the end of the day we return that list of out of stock products here's another really similar example where we're trying to figure out what are the expensive prod our system so we start with an empty array we iterate over the products we check to see is this product more than 100 dollars or 100 euros if it is we stick it in the empty array build that up and ultimately return that at the end of the day so we have a new array that's just the products that cost more than $100 so similarly if we generalize the names of everything except the piece that's kind of different in each one we end up with code that's exactly the same except for the contents of this conditional like the actual condition that we're trying to verify the only difference is in this one we're checking to see if the price is greater than 100 and this one we're checking to see if the item is out of stock we can do the same work to extract that into a call back execute the call back inside the loop and now we have two pieces of code that are again exactly the same except for you know what's being passed in through the callback so now that we have two pieces of code that are exactly the same we can pull that out wrap it in a function and we've implemented a rate filter from first principles so if we take this example the out of stock products where we're doing it in a loop we can replace that with just a call to a rate filter where now instead of having to duplicate the specification of how we're doing the iteration and all that stuff the only thing that we actually have to specify is the condition that we need to satisfy in order for this item to be included in the new list so in this example we get to remove everything except the one piece of code that mattered for what we were doing and now we have like a generalized abstraction built around this pattern that we can reuse in different places instead of kind of duplicating that iteration code all the time but what about an example like this so this is a little bit more complicated so we're trying to get the email addresses of the employees in the sales department so we start with an empty array of emails we loop over all the employees we check to see if the employees department is the sales department then we want to add that employees email to our list of email addresses and then ultimately we return them so if you look at just this piece of code this looks a lot like map right very similar to the map operation that we created earlier but this piece of code here kind of gets in the way and prevents us from using map if we were to try and use map in this situation it would give us the emails of every single employee not just the employees in the sales department so how can we get around that when I run into a situation like this the first thing I try and do is figure out how I can solve this problem in more discreet independent small steps right and my thinking process is something like well I said to myself you know I can't use map in this situation because if I used map it would be applied to every employee not just the employees in the sales department I can kind of flip that kind of negative idea around into well I could use map if I was only working with the employees in the sales department so how can we change this bit of code a little bit such that we can get ourselves in a position where we're only working with the employees in the sales department well that sounds like a job for a red filter oh all right there we go still good so now we can filter out any employees who are not in the sales department and now we have a list of sales employees that we're going to store in this new variable and now if we look down at the code below we can replace our reference to all the employees with a reference to just the sales employees and now this conditional becomes totally redundant because we already know that we're working with employees that are in the sales department so it can go away and now we can look at this loop at the bottom and all of a sudden we realize hey this is a ray map we can totally replace this with a ray map so we take that loop swap it out with a call to a Rhema or we're just mapping all the remaining employees into their emails and now we're left with this solution which no longer has any loops or any conditionals which is kind of cool compared to the original solution that we had so I want to take a little bit of a break here to talk about some things that I hate the first is for loops if you haven't gathered already I'd hate for loops the second is conditionals I hate conditionals and so far we're doing a good job of following you know avoiding both of these two things that I hate but the third one is temporary variables some of the cornerstones of programming for loops conditionals and temporary variables if we look at this code that we have here we have no loops no conditionals but we have not one but two horrible evil temporary variables so how do you get rid of temporary variables well a common refactoring that you can use to do that is called inline temp and basically the way inline temp works is you find every reference to a variable and replace that variable with the value that that variable is holding and this works especially well in situations where you only are using that variable once because otherwise you end up with duplication right now in this case the only place that we're using the emails variable is right at the bottom here where we're returning in so to inline that variable with the value that it's holding all we have to do is return the array map results directly and now that emails variable completely goes away now sales employees the only place we're using that is as a parameter to array map right but if we try and inline the value of sales employees which is the array filter call up here we end up with code that looks like this which to me isn't super great it's a little bit confusing and the reason that I find it confusing is because we want to filter the employees and then map those employees into the email addresses but that's not what our code looks like it's saying at first glance our code says map and then below it it says filter which is a little bit confusing like that's backwards to how we're actually trying to apply these operations and I want to talk a little bit about why that is so this is something I call the problem with primitives and I'm going to show you an example using strings instead of arrays so here's a function that takes a camel kick or a sorry a snake case string like hello there Larry Khan and converts that to a camel case string and looking at this at first glance it's pretty confusing right like what's happening here what's getting called first what's getting called last what's parameters to which and the way this is working is first we're calling stir replace down on the fourth line they're passing the result of that too you see words passing the result of that to another store place call and then passing the result of that to LZ first to lower case the first character this takes a little bit of time to grok if you're just looking at all these nested function and the reason for that is that you have to kind of read it inside out you have to start at the beginning and kind of count the parentheses until you see a closing one and then you know okay so this is the first thing that's being executed and that gets passed to this which gets passed to this it's hard to follow compare that to this kind of make-believe version imagine that strings in PHP were objects that had methods we could take the snake string we could replace the underscores with spaces then we get uppercase the words then we can replace the spaces with an empty string effectively eliminating the spaces and then a lowercase the first character and that would give us our camelcase string to me this is a lot easier to read and a lot easier to follow than the version that we looked at before now it's hard to do this with strings in PHP but it's definitely something that we can do with arrays and we can do that using collection objects so collection objects are an object designed to wrap around an array to expose the operations that we can perform on that array as methods on the collection and laravel has a great collection class built-in that you probably run into a lot when you're working with laravel like anything that you get back from eloquent comes back as a collection anything that you get back from the query builder even in 5.3 is a collection you run into them a lot they're really cool so let's see how using a collection would have changed this code so let's look at this first piece here where we're taking our employees and we're passing it as a parameter to array filter along with the operation that we want to use to filter instead of taking employees and using it as a parameter to a function we can treat employees as an object and call filter as a method directly on an employee's collection and that's going to return that array or a collection of sales employees looking at the array map call we can do the same thing instead of passing sales employees as an argument to array map we can call map on the sales employees collection that's we'll return a collection of the emails now if we try and do that same in line temporary factoring we end up with code that looks like this where we're saying take the employees filter them down to just the employees in the sales department and then map those employees into their email addresses which is a lot easier to follow then this example where everything is kind of in backwards order and you imagine how much worse this gets when you start dealing with more than two operations and compared to this solution it's kind of cool because there's no details about loops there's no details about conditions and said it's just kind of an expressive declarative way to say filter out any employees that are in the sales department map those employees into their email addresses kind of cool so when I put slides let's write some real code so I have a little layer of OLAP setup here where I've got some code that I want to refactor using this collection pipeline style into something a little bit more expressive and hopefully free of loops conditionals and temporary variables so this is that piece of code that we looked at in the very first slide right after I kind of introduced the talk and I'm going to spend a minute here to kind of explain what it's trying to do so the goal of this piece of code is given some employees that work for this company where each employee maybe works in a different district and each employee serves some group of customers we want to figure out which customers that were served by employees in the Antonine district how much total sales did our best customers bring in so how much money did we make from our customers in Amsterdam who placed at least $75,000 worth of orders and you can see it's like kind of this gross nested shape with lots of temporary variables and kind of hard to figure out how how it really works so if we go to the browser here and refresh this you can kind of see this is kind of our benchmark this is the number we're going to be comparing against to make sure that we're not making any mistakes or screwing anything up and I have another route down here this is the great route whereas this one is the grim route so we're going to go from grim to great in this presentation where I have that same implementation but we're going to slowly refactor it and hopefully returning the exact same results along the way so how can we get started here and as we go over this we're going to talk about a lot more operations than just filter and map and I'm just going to kind of introduce them as we go and talk them a little bit and should be fun so looking at the set of nested loops and conditionals and stuff what is kind of the first thing that sticks out to me that I would want to try and fix the first thing I see is this conditional here right we have all this code here kind of like the real meat of the logic lives deeply nested down inside of this function it's only being run if the employee that we're iterating over is in the amsterdam district so one way that we could kind of try and remove some of this indentation and remove some of this you know complex logic would be if we could figure out a way to only be dealing with employees who work in the amsterdam district in the first place so that's sounds like a job for a ray filter right so why don't we do something like amsterdam employees equals employees filter and this is going to take the employee and then we just need to specify the condition that's going to verify that this employees in the answer name district so we can just return employee district equals Esther dam and if we dump that out just to kind of make sure that we didn't make any silly mistakes we can hopefully verify that we only have employees in the amsterdam district so we have six employees and it looks like this guy's in the instream district and tram district I think we're okay so now that we're working with employees that are only in the amsterdam district we can simplify some of this code here so instead of looping over all of the employees we can just loop over the amsterdam employees I always want to capitalize that D for some reason and we can Amsterdam we can get rid of this variable assignment here in that condition and we can pull this out a little bit and if we refresh this get rid of that dump looks like we still have the same amount the 2.9 million dollars so what's the next thing that we can do here so the next kind of piece of indented code here it looks like we're trying to get all of the customers of an employee and then loop over those customers and do something I don't really know what yet so how could we kind of get this code out of here well it looks like it might be easier if we had just a big list of the customers like if we had all the employees and all of their customers put into one big list then we could just iterate over all those customers and then we might be able to kind of bring some of this code back to the left side of the screen a little bit so how can we do that let's think so let's create a variable called something like all customers and if we want to take the employees and transform those employees into lists of the customers that sounds like a map operation right so let's try and map and see what happens so we can say Amsterdam employees map that's a function that takes a employee and we want to return that employees customers let's dump that out and see what we have so now we get an array that's 6 in size again which is kind of suspicious to me because we only had 6 employees you think that employees would have more than one customer each but if you look inside this is a collection of six collections where each collection is made up of customers so rather than getting one big list of customers what we actually ended up with was a list of lists of the employees customers right which is not quite what we need so how can we solve this problem well there's another array operation that we can talk about here called flatten that lets you take an array of nested arrays or collection of nested collections and flatten it down some arbitrary level and laravel collection class provides method for us and lets us parameterize the level to which we want to flatten in so by default if we just called flatten on this list of customers it would flatten it recursively all the way down to the bottom which is not always what you want but we can just pass one as an argument and it's only going to flatten it one level which is going to bring us just this big list of customers so if we refresh this now now we get an array that's 37 in length that's all customers all right perfect so if we set all customers to be this so we'll flatten it right at the end here and get rid of this dump and now it looks like we can get rid of this loop that's looping over the employee's rights the only reason we need it to loop over the employees was to loop over their customers so we get rid of that get rid of this bring this out another level and now instead of customers will call it all customers since I way we named our new variable and if we didn't make any mistakes when we refresh the browser we should be back to our 2.9 million dollars cool so the code that we've got down here is already starting to look you know quite a bit simpler than this thing monstrosity so what's the next thing that we want to do here so let's kind of figure out what this code is doing so we're looping over all the customers we're initializing this customer total sales to zero we're looping over all of a customer's orders and adding the value of those orders up and building that up in this customer total sales variable and then we check to see if the customers total sales are greater than or equal to like our threshold here which is 75,000 then we include that customers total sales in the total revenue that we're building up and ultimately returning which we initialized up here so it sounds like this might be easier if instead of having to iterate over all these customers and then get that customer's orders and add them all together and then filter out the ones that are not meeting our threshold if we just maybe had a list of the total value in orders that each customer had placed so if we could somehow take this of customers and transform that list of customers into a list of the dollar values that that customer had spent with us in total over the you know the lifecycle of this report of a date range that we're checking this in so that kind of sounds like a map operation again right we want to take the customers we want to map those customers into the total amount of money each customer spent it's like a one-to-one relationship so let's try and do that so let's create a new variable we'll call it something like customer totals and we'll take all the customers and we'll map those customers so we're going to have a map function that takes the customers and arguments and then we have to figure out what we're going to do in here so maybe the easiest way to do that to get started would just be to kind of copy this code out of here and see what we get so we're in initialize customer total sales to zero we're going to iterate over that customers orders add that up and if we return that customer total sales with any luck when we dump this we should get a collection of the values that that customer you know has spend with us so we get a collection that's 37 long again which is a good sign because we had 37 total customers and if we check it looks like yeah these look like reasonable numbers we won't know till we get a little bit further in the process here but it looks like what I would expect it's 37 numbers that looks like it represents the total amount that that customer spent with us so now that we have that well let's keep that there for a second I don't really like what this code looks like right we're trying to get rid of loops we're trying to get rid of temporary variables that's not what we're really doing here so how can we simplify this kind of inner set of loops and stuff like that well let's think about it so we have customers who have orders and each one of those orders has a total and we kind of want to add all those totals together right well it would be easier to add the totals together if we just had a list of all these totals so why don't we map the orders into their totals so we could say something like order totals equals customer orders map let's make some room down here this is going to take a function that takes an order and now we want to return that orders total and now that we have a list of order totals we just have to figure out a way to sum up these order totals now what we're really trying to do here is take this list of order totals and like reduce that down into a single value and there's an operation that the collection provides us that's a standard functional kind of array operation called reduce it's kind of a confusing one but it will let us reduce this list of values down into one single value so if we call reduce here let's do a new variable we'll say total order value equals order totals reduce now reduce takes a callback that takes a value that we're kind of accumulating that a lot of people call the accumulator in this case because we're kind of trying to figure out what the you know total order value is a lot of time what I'll do is I'll just use the same variable name here and then it's going to take that individual order total and then whatever we return from here is what gets passed to the next one and how it kind of builds up so this is kind of a confusing one I'll show you how it works but then I'll also show you how we can simplify it a little bit so here we want to return the current total order value you know whatever was when we were looking at the previous item we want to add that to the order total of the current one and we need to say that we want to start with zero as the initial value that we're accumulating from and with any luck if we dump this total order value hopefully we don't get some weird error cuz I made some silly mistake so there's a number that hopefully represents the total amount that some customers spent with us now because this is such a common use case for reduced like using it to add up a bunch of values there's actually a shortcut for it called sum or instead of using reduced directly we can just call sum which is you know behind-the-scenes sort of implemented use and reduce essentially and if we dump this out again hopefully we see a familiar value and it's the same thing now in our efforts to get rid of temporary variables we can use our inline temporary factoring again to take this code here replace the order totals with that code dump this out and we get the same value now this is still looking a little bit not great we still got this sort of like you know triangle thing going on here like first of all all this code doesn't matter anymore right we can just return this directly here dump the customer totals and we kind of get what we had before but we still got that kind of like multiple levels of indentation thing going on that would be cool to get rid of well this use case of map where you're just taking some object and trying to extract one property from that object is so common that laravel provides a helper on top of that called pluck and we can use pluck to just say we want the total so now we're going to pluck the total from each orders which gives us a list of order totals sum those up and we get the same thing and now this use case of plucking something and then summing it is also so common that we can actually just pass the string to total or to sum sorry so we can just say the customer totals is equal to mapping the customers and then we take the customer and we sum up the total of all those orders and we get the same thing much shorter so that's looking pretty good so what can we do next here well now we can change this loop to instead of looping over all customers we can loop over customer total as customer total and all this code goes away and we can just say if customer total is greater than our men's sales then our total revenue is going to include that customer total and with any luck if we refresh the browser we're back to our 2.9 million all right same answer that we started with looking pretty good so how can we get rid of this code well it looks like what we're trying to do here is filter out any customer totals that are not greater than or equal to our threshold right so we can do something like best customer totals equals customer totals filter and we just have to return this condition here so return that the customer total is greater than or equal to min sales now we have to do this disgusting horrible thing page P makes us to where we use this min sales variable and let's dump our best customer totals here and with any luck this should be a list smaller than 37 so now we have 23 customers that paid us more than the threshold that we are hoping for right so now that we have our best customers our total revenue can actually just be equal to the best customer totals and then sum them up again just like we did when we were summing up the order totals all Matco goes away and now we have that 2.9 million that we started with so we've gone rid of all of our loops and all of our conditionals but we're left with some temporary variables so let's walk through inlining some of these temporary variables best customer totals only gets used in one place so we can take the value of that and replace that there refresh the browser 2.9 million customer totals only gets used in one place so we can inline the value of that as well get rid of that still get the same answer all customers only gets used in one place so we can inline that and get rid of this still 2.9 million and amsterdam employees only gets used in one place so we can cut that in line that delete that we still get the same value and now one thing I forgot to mention here is that this situation here where we're mapping something and then flattening it by one level is also so common that it has its own operation called flatmap so we can just call flatmap get rid of this Fontan call refresh still get the same value total revenue doesn't need to be initialized anymore the only thing that really matters is this min sales thing and I think I'd be quite happy to either inline that or use a constant in this case so we don't really need that temporary variable that goes away and we have the same answer and we only have a max one level of indentation we have you know no temporary variables except kind of the setup and ending ones that we're kind of using just kind of get our initial data and return our value but in terms of like calculating this value we didn't need to create temporary variables and build them up and store them or anything like that so no conditionals no temporary variables no loops it's a lot kind of nicer looking than this code which to me matters and we've kind of solved the exact same problem like a totally different way where now every single step of this can kind of easily be understood on its own there's no kind of like shared state to worry about so here we're doing things like you know adding total revenue to customer total sales which is initialized here and incremented here and total revenue is initialized up here because that's the scope that it needs to exist in and here you only ever have to worry about like okay filter I know what that does and I know what that gives me at the end ok flatmap that does one discrete thing on its own it doesn't have to worry about some state here or stake here or anything like that and you just kind of go through every single step and these small you know independent discrete steps and it leads you to this cool functional solution that looks kind of totally different which i think is really cool so we've got 10 minutes left so I have some other cool stuff to show you one thing I would like to mention is that a common thing that people mention to me and a common thing that like I'll even say is this is kind of the best like idiomatic way to kind of teach these principles right in some of the cool ways that you can talk about collection operations and stuff but a lot of people would say like this is the sort of thing that you should just be doing in the database anyway so right like you want to load all this stuff up into memory and like figure all this stuff out so I want to show you some cool ways that I use collections that are not really related to this idea of taking some big data set and trying to like do something with it more just like day-to-day ways that I use collections with even just like really small lists of things and things that I do to kind of make my code a little bit more expressive and a little bit more you know to me maintainable and easier to follow so a little while back we got a pull request to laravel valet where someone want to add support for PHP seven one or two at least have PHP seven one work with valet and the code that they submitted is exactly what I've wanted them to submit they just kind of follow the existing pattern that we had set up in the code base where you know we're originally checking if PHP seven is installed we consider it valid if PHP five six is installed it's good if five five is installed it's good so he just added you know another item to that list and the same sort of thing down here and you know it's just what I would have wanted someone to do just kind of follow the pattern that we had existing but when I saw this come in it really made me realize that you know there's a better way for us to structure this code so I want to show you some cool ways that you can use collections in this case so we're going to do some live refactoring of like some real code here so here's this function that checks to see if we have an installed version of PHP so the first thing that comes to mind when I see doing something like this with this list of items I'd like to figure out a way to actually do this with a real list operation so the first thing I want to do is create like a temporary variable here called supported versions and supported versions is going to be a collection of all the versions of PHP that we supports and now that we have this list of supported versions one way that you might rewrite the same code is to do something like this right for each supported versions as version if this installed version return true and if not if nothing matches return false right and if I run our valley tests I should still pass everything's still good but we don't like loops right we want to get rid of loops so how can we get rid of this stuff well has anyone ever used like the an array function in PHP yeah someone has used an array come on I know it's the end of the day and people are tired and grumpy and they want to drink beer and eat barbecue but come on work with me a little bit so the int array function takes like some value that you're looking for and then like some list of candidates that you're trying to see if that value exists in that list of candidates right collections have like a similar function called contains or I could say like supported versions contains and then pass it some value so I could say like the supported versions contain PHP seven one and this will return true because it does now the cool thing about contains that makes it way more powerful than just looking for a single value is that you can also look for any value that satisfies some condition that you specify by passing in a callback so I could say supported version contains some version where this returns true so the supported versions contain a version where this installed version returns true and I can get rid of all this code and just return that directly and if I did it right Oh installed what did I'm mistake did I make here this installed someone know oh I know why this is this is actually an interesting talking point so one of the breaking changes in laravel 5.3 is that we switch the parameter order it contains because this is a mistake that like people run into all the time so the contains callback actually takes the key and the value of the array and you pretty much never need the key and you always forget that you have to put it first so that got swept swamped in 5-3 and we're still on the 5-2 version here so common finna do is issues like a variable called underscore for useless variables that I'm forced to declare that I don't actually want to use and if we did this correctly now we get tossing tests cool one more thing we got five minutes so I want to get through one more real quick the other situation was like down here which starts to look even Messier we're trying to figure out what like the currently linked version of PHP is with homebrew so we're basically just going over all these different candidates and checking you know if the PHP that we found matches this name than that's the PHP that we want to return so what's another way that we could write this so first let's take this supported versions thing because that's something that we're going to need again and extract that out into like a little helper function supported versions and we'll just have this return this collection and now this can go away and this just becomes return this supported versions contains a version that's installed which is pretty expressive and kind of cool sorry parenthesis that's fine come on so still good if we jump back down to this link to PHP function now how can we kind of do this in like a listy way right so if we want to do something in a more erase sort of style we might implement it with a loop in the this sort of thing right we may do something like for each this supported versions with parentheses as version and then we basically want to do like this thing this gross stir pause is like the worst washing effort so instead of just hard coding PHP seven one there we can say version and if that does not equal false then we want to return that version right and if none of those match then we want to defaults to throwing this domain exception on those again still good so we don't want to do this with loops we want to do this with collections right so what are we actually trying to do here if we were trying to describe this in like a more expressive way to me what we're trying to do is find the first supported version where this ends up returning true and the collection has a first method that takes a call back that lets us do this or a cool thing so we could say something like linked version equals this supported versions first and first has that same stupid API that takes this unused variable and we want to return the first one where this condition you know is valid and we're going to need to use the resolved path now the only thing that kind of sucks about this solution is that down here now we have to do something like if linked version is null throw the exception right and if not return the linked version so it's still kind of cool that we can use first this way but this parts sort of grim now let's run the test to make sure I'm not totally messing things up again so the cool thing about first is it lets you specify a default right so if it doesn't find any that match that condition you can say return this instead you can do that by passing some value here you have some default like maybe we want to say if no versions of PHP are installed for 10 PHP t6 is installed it doesn't make any sense but like that's the sort of thing you could do it's not really helpful in this case though is it because we don't have like a default that we want to return instead we want to throw this exception well something cool about the default parameter here is that I can also take a callback that's lazily evaluated so say that you wanted the default to be something that was expensive to do like some database query or something you could put that here and it wouldn't get run unless the default was the only option to return a cool trick that I haven't seen many other people do here is that you can actually just throw this exception in there who gives a and now this goes away and when it executes that callback it's going to throw that exception and we can get rid of all that if statements and temporary variables and stuff just return that right away and just throw that exception if it doesn't find anything and it's still good so I thought that stuff was important to cover because I think it's a more realistic use case of like how I use collections in my day-to-day programming people always ask me questions like aren't you worried about performance you're doing multiple loops or you know you have all these big data sets in memory blah blah blah blah blah and it's like if you're actually using them that way then maybe that's a problem but most of the time I'm just using collections to be able to write more expressive code that lets me use operations that exist on the collection to express the things that I'm trying to do in kind of a more object-oriented way and yeah so here's an example I guess of how I use collections most of the time and some of the cool little refactorings that you can do with them as well as hopefully what was a useful introduction to functional programming thinking in general as well as kind of teaching you how to think about how higher-order functions can help you create more powerful and useful abstractions in your code so that's all I have one thing that I did actually want to mention I don't want to sell it too hard but if you if you enjoyed this and you want to learn more I wrote a book and released a set of screencasts cover more of this stuff there's a 25% off coupon for people who attended the conference if you if you want to check it out so as a bitly link it's called refactoring to collections it's either at that link or at adam web and ami slash refactoring - - - collections and i also host a podcast called full stack radio that if you haven't checked out check out the sick animation love keith house have you never checked it out you should i get to interview lots of really smart people I've had like Michael feathers on there talking to me about collection pipelines ever interviewed Kent back I've had DHH on there twice I think he's the coolest I don't know about you guys but it was pretty awesome so yeah that's been pretty awesome to be able to do that and if you're interested in hearing discussions with people but that sort of thing check it out and yeah I'm Adam Wyman on twitter my website is out in Miami where I blog about how to charge a design and testing and stuff like that and if you can rate the talk on joined in at that link I'd love to hear any feedback that you have and uh thank you and I hope you had an awesome time you
Info
Channel: Laracon EU
Views: 15,103
Rating: undefined out of 5
Keywords: laravel, laracon, amsterdam, php
Id: crSUWtRYw-M
Channel Id: undefined
Length: 45min 31sec (2731 seconds)
Published: Fri Oct 07 2016
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.