Functional Programming in PHP

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
thank you and so I'm Eric poncho I do work at principle they put me in the Meredith room for some reason that's okay I'm going to be switching back and forth between my slides and doing live coding today but all the codes on when I write is on the slides so I have the URL for the slides I'll tweet it out after it's also on the last slide if you want to catch it then the other thing is I'm live coding I hold for sure making mistakes if you see it shout it out help me out that would be great but we can't figure it out together and we'll just switch back to the slides and see what the code is so all right let's get started so sorry torna my coworkers the other day David we've been prepping for this talk we were talking about PHP and he likened it as the English of programming languages and I thought that's pretty funny we started thinking about it more and kind of joking about it and said PHP is like English because it borrows things from other languages and so one example the PHP would be generators they borrowed that syntax from Python another one that I like is the spaceship operator which is my favorite Lee named operator that was borrowed from Perl I'm ENGLISH borrows everything from another language right the other thing with PHP is that it has its part of these things it's applied those rules very consistently and so we'll see you later when we're looking at some of the arrays functions that some of the questions take there is the first parameter some ticket that's the last parameter so mutate the array so don't you kind of just have to learn so every way that you have to learn weird things in English like the plural of goose or moose then lastly PHP has a lot of ways to say the same thing you can solve the same problem in many different ways same with English right I'm in this last bullet point I think is what makes PHP a fun language you can be like Drupal and go all in on object-oriented right classes for everything or you can solve the same problems but more in a more functional style of programming and so that's what we're going to explore today so the first example we're going to look at is being lazy which think there's something I like to do right so we're gonna switch over to my code here and take a look at this example we have a function that I call expensive calc I hold now reset to its starting spot and let's pretend an expensive calc does something more than just print called it does a whole lot of calculations it's doing some real number crunching and then it's finally going to return a result when we call expensive calc later in our code down in this conditional my question is for everyone I'm going to do a little quiz show of hands how many people say expensive count gets called at this point anybody nobody so assume everyone done things expensive calc doesn't get called and we can verify that really quick so let's run that down we're terminal lazy PHP and we can see in fact caldas never printed so it wasn't called anyone want to shout out the reason why it wasn't but X is false and so expensive Calcutta called because of short-circuiting so the an operator in PHP like many other languages will evaluate the left hand side of the expression first but that's false there's no way the whole expression could be true so it never evaluates the second half and so expensive Calchas not called can we make a slight change to our program though and take expensive calc out and store it in a variable now what's gonna happen we call we're gonna have to get this again we see that expensive calc is called and even though Y is never used anywhere in this code it's dead code PHP doesn't know to not call expense account to understand what it is we need to look at a concept of QR functions so all of the terminology in here and the definitions I just grabbed off of Wikipedia will explain that a little bit more so this is from Wikipedia pure function has these following two properties one its return value is the same if its arguments are the same so that means every time you call that function if the inputs you pass into that function are the same that output is going to be the same the second property that a pure function must follow is that it doesn't have any side effects so side effect is really that your function is doing something outside of its scope so we'll take a look what that means in a second but just for your mental model a pure function is like the functions that you learned about in algebra or in your high school math classes like y equals f of X all right for any given X you have a Y and if you for that X you'll always get that line all right so let's look at some example of impure functions that you can do in PHP so this first one here violates our first rule where if we were to call global scope it's dependent on this global variable X so every time we call global scope we could get a different result if that global variable changed here's another example that violates the first rule by using the static keyword count is now kind of locked in so when we call this function over and over again we'll get a different return value because count was defined as a static variable here's an example that violates the second rule where we have a side-effect the sine of X years that we're writing to a file or pending the word hello to a file and finally here's another example of where we have a side-effect we've passed in this array by a reference and so actually modifying the array that's outside of this function scope so flipping back to this example here the reason that PHP is running expensive calc right here is that it doesn't know this is the pure and impure function so it has to assume this impure and then it might be doing something other than setting its value to Y so even though Y is never used and it could be doing something else so it runs so what we want to do is change our code a little bit so again we're not running expensive calc if X is false so one thing that we can do is simply wrap this in a anonymous function and then call why down here so now made why a function that returns the result rather than just the result itself the reason that we're able to do that is a property of pure functions called referential transparency and so something is referentially transparent when you have an expression that can be replaced by its evaluated value and it means the exact same thing and so just for easy examples to understand that 2+2 can be replaced by 4 they mean the same thing and that's a more complicated expression if you concatenate string together you can replace either of these two are the same a little bit more complicated and immediately invoked function could be replaced by just its return value and finally the one that we care about is that a pure function can be called and it can be replaced by its return value so add 1 plus 3 since that's a pure function we can replace add 1 plus 3 with 4 and vice versa so what we're doing here is we're replacing this function call with the value they can be interchanged without any real change to our program but what we want to do here is rather than make our why more complicated we would still like to just call expensive calc and so instead of having that anonymous function on our Y we're going to move it up to our expensive cap and put all of our expensive logic now with Indus returned anonymous function and so because we're still making a function call down here it will only run this inner function yeah X is true and it's not in this case though it won't be called now we have this function that returns a function which is a little bit weird the reason that we are allowed to do that in PHP as that functions are considered first-class so anywhere that we could use a value of variable we could also use a function so a function can be the return value of a function it can be passed it as a function like it can be passed in as an argument to a function it can be assigned to a variable well that allows us to do is create higher-order functions so expensive calc now is a higher-order function and so the definition of that is higher-order function is a function that does at least one of these two things that could do both but that is takes a function as an argument or returns a function as its results really simple examples here's the function that takes a function that's a higher-order function then here's a function that returns a function so that's what we have here at the expense account we have a function there are transfer function now let's say we wanted to pass in a value all right our expensive calculation took some sort of parameter and so we'll simulate that by saying our print here is that going to pass or putting out an A in order to do that we're going to need to take an A we'll have to pass it in down here then we'll need to make extra to actually see this happen and so now it says called 7 because we passed in 7 but again we wanted to keep our kind of condition clean and move our logic out of it so what I'd really like to do is pass in 7 right here and two other calls or expensive calculate but when I move my variable up to expensive calc call and we run this code again we're gonna get an error really a warning that our a here is undefined this return function doesn't have access to its outer scope and so the way to gain access is to use the use keyword and so we can say I want to use the variable a of this outer scope and lock it into the scope of this inner function so now when we run this code we should get called 7 again but using the use keyword we've now created what's called a closure with this inner function and so every anonymous function and PHP extends from the closure class so capital C closure what I'm talking about here is not that class of closure but rather the concept of closure which is a function that is bound to an environment so it has specific variables that are locked in they're bound to it and so the use keyword is what creates keys we saw that if we try to access a variable outside of its scope and in an anonymous function that doesn't work but when we used the use keyword we have that variable in its local scope and the interesting thing about it is that variables value gets locked in at the time that we've defined the function so our greet function here is using X which is currently set to high if we change X to hello and call greet again we'll still get high that has been X has been locked in at high at that point so we can use higher order functions to create closures so here's how we could rewrite great to take in the value that we want to lock in so great here we can call a PI to create a new function a new closure called say hi that when we call hi there's the same example or okasha hello and that will be the value lifetime all right so just trying to close off this example we saw that instead of passing just a value we can pass function calls in two different pieces of our code and that will allow us to evaluate that function only when we need to PHP allows you to do short-circuiting there's other ways that you could defer the actual calculation just by passing a function instead of passing its result all right now we're going to move in to working with numbers and I'm gonna start it's gonna be kind of a contrived example but let's imagine you we're working one day and your boss came and said I would like you to create a function that increments a number by one and of course you can do that very easily you create a function called add one it's gonna take a number a and then it will return a plus one don't even need to test that we just know that's gonna work very simple right then they come back your boss and says ah now I need one to also increment by 2 so your first instinct as a good developer is to copy and paste and just replace the one here both of the ones with it to call it a day but of course they're gonna come back again and ask for three and four and five and so on and so you maybe will copy one more time and then think back to yourself this codes not very dry I'm repeating myself I'm gonna use some technique we just talked about do create something more reusable and so we're going to use that pattern where we have a higher order function the creates a closure and we're able to lock in the value that we want to increment so I create a function and we'll call it add Factory is we're going to be creating add functions as a lot is going to take our increment the value we want to increment by just call it B we're going to return a function that takes our a now we need to use our B then it will return a plus and now we can rewrite our add one add two and add three by using our factory that creates our closure so now add one is going to equal our add factory we'll pass in one we'll do the same for two and three so we'll just place the numbers there and we can actually will actually test this out to make sure our add factory works so add three blue siree we should get sex oops and we do six yes so I'm feeling pretty good about ourselves right we used this new pattern that we learn to I'm create a bunch of add functions and then I Ross come back what says I want the exact same thing for multiplication and our instinct might be to copy this and then just replace the word add everywhere with times let's do that that and then change the plus sign to multiplication you know we're good but we know in our heart they're gonna come back and say I want the same thing for subtraction and division let's see if we can create a generic way to create our factories so I want to first do is move these to right next to each other so we could take a look at both of them and look at their difference we're not the only difference is that on the top one we're adding and the bottom one we're multiplying and so what we can do instead of having these hard-coded into two separate things because we can abstract that out and pass in the operation that we want to execute so let's first create functions for both will create an ADD function that'll take our a and our B and add them together and then we'll create our x function it also takes an A and a B works and multiplies them and now we'll get rid of our specific ad and times factories okay angry instead we'll call it a generic factory and that is going to take the operation that we want to perform as a function it'll still take an hour be overturning a function that takes in are a way to use both the function and the B and we'll return a call to our function with a and now we can change all of our ad factories to use a generic factory with our add function and the same things for our times factory is our generic factory with e times function our generic factor that we created is an example of using partial application so let's look at the definition here a partial application so refers to the process of fixing a number of arguments to a function then producing another function of smaller ard a narrative means the number of arguments that it takes so we're doing the partial application that we take an original function we are locking in one or more of the arguments to that function returning a new function that takes the rest of the arguments that when called will call the original function then with all the arguments what that looks like right now and with our generic Factory we're only taking in one argument here's a example of how you could write a function that would work for any number of arguments so here we're using the splat operator to take in any number of arguments and storing those off and all right then we'll return a function that again takes any number of arguments it stores those off and then we're able to merge both sets of arguments together and pass those into our original function so that wouldn't work with our add function to create an add one but it also work with a function that takes then more than two parameters this one takes in three and when so we can use partial full name passive Eric which would only LA and the first name variable so when we were if we were to call Eric's only we'd then pass in the middle in the last we could also create a function Erik Em's only where we've locked in both Eric and Adam as the first two arguments so we call this we would only be passing in the last night so we could go back to our code get rid of generic factory everywhere we can change all these references to partial and just to prove that it works let's print it out times 3 or 3 we should get all right so now that we've created these different functions that increment and multiply let's use them together in some way so let's say now we want to take a number add one to it and then multiply it by two so we'll start off with the number x equals two we'll create a temporary variable Y that does add one then we'll have another variable Z that there's all of times 2 of Y then we can print so let's go ahead and do that and we get sick so I do plus 1 is 3 times 2 SX this looks a little ugly right we've created these temporary variables that we didn't really need so we can move the call of a droite into there get rid of our Y and this will run exactly the same but this solution isn't very reusable all right we can only call it a one time with RS so if we wanted to create a function that does both times to an ad while we need to write that a little bit differently so let's try to do that function add 1 and x 2 that'll take an X and we want to return basically what we just have write x 2 1 let's actually try to run this code it'll see that we get oh we need to actually call the function we'll see that we get a bunch of errors about not knowing what these variables are again that's because these variables were defined outside of the scope of this function so can't access them so if we try to do a use here that also doesn't work because you can only be used on anonymous functions all our closures so we're needed to find this as a variable instead and call it like this until that will work now but I don't know that this is any better it looks uglier I think and so what we're going to do then is turn to classes to hopefully provide us a better interface so I know it's like blasphemy to use classes we're talking about functional programming but we're going to use a class that's going to help provide us a better interface for working with functions and our values so class that we're going to create is going to be a box and it's going to do what it sounds like it's going to provide a box around a value so we're going to create a probable value of x we're actually going to create a private constructor [Music] that will only be called from inside itself quite a private constructor so we can't ever call new box still as its functional program but we don't ever want to instantiate a new class or self we're gonna want a function that we're able to call to do that so we'll create a public static function called of as what we want to do is create a box of a specific value and here's the look where we can call or actually call them new but we've abstracted that away from the user of our class they don't ever need to call new they'll interface with us yeah ugh we'll need a way to unbox our value to get it back out of the box and so we could create a function called unbox but instead we'll use like a PHP trick and use in bulk so we'll be able to just put two parenthesis on the end of our box and I'll spit the value background and so here we'll return the actual value now this box doesn't do anything Yeah right we're just storing a value and then being able to get it back out and so the way that we're going to be able to interface with our value because we're going to create a function called map it will allow us to pass in a function and have that function applied to our value let's create that map it's going to take function and we're going to return a new box where we have called that function with our axe so this is giving us an interface to apply a function to a value and then return a new box and then we can get the value out of the next let's go ahead and use that here will wipe out what we did down here and instead use our box so it's a box of 2 and then we can map with a function so we'll use our add 1 than times 2 and then to get the value out after in boquete so there's a little trick and we can run this code let's go look at our box where is that round right there and we're on that again and we shouldn't get for the side we get sex good the interesting thing about this map method has the associative property where we can instead of composing our functions together like we did we can chain that and so not going to go into I've been doing some functional programming terminology go won't get into the really deep stuff but our box is considered a func door so if you want to look that up in your own time you can but functors have this property with maps the instead of having to compose the functions first you can just chain your maps together and so we're left with this really nice interface for a box where we can really explicitly say what's happening to our value as we pass it through these functions so we start off with a box of two then we're going to add one to it then we're going to multiply it by two and it reads almost like English even though there's some weird symbols in there to someone who is a programmer and they could maybe still figure out what's going on with this code all right let's move on so we went over single values our numbers and now we're going to deal with already so we have a list of numbers instead alright for this exercise what we're going to do is we have an array 1 2 5 and this is kind of a classic functional programming example now we're going to find the sum of the odd squares it's order to get the squares of every value in our array take just the odd ones that add them together and so how we would probably first start off trying to do this is writing a for loop and when writing a for loop I always have to pause twice the first time is to remember is it less than or less than or equals to plus 10 and the other is to figure out how do I get the length of my array having a lot of other languages it's either length or size the PHP it's count and then the inside of our a will kind of do those three things that we talked about we're going to get the square of our value so I'll just call that square that'll be equal to our array index I and we will square it then we'll check if it's odd by using the pot operator so an even number my two is zero so that I'll be false and I remember my two as one so that'll be true then if it is true then we will take our total and all say total plus square all right let's print out our total down here and we'll see what we get that's 35 we can check the math really quick if we square we'll have 1 4 9 16 25 the odds are 1 9 25 1 plus 9 is 10 plus 25 is 35 so our code worked in order to read this code though we kind of have to step through it line by line it's very imperative style of coding right we saw in our box example with our numbers we want a more declarative cell where we can just kind of read through skin and almost read in English to figure out what's going on over there code well we can take a first stab with us by applying some functions so I've actually already written out the functions that I want to use and so I have a square function it's gonna square a value and his odd function that will take value and tell us if it's hot or not then finally an add function they will add two numbers together so we can go back to our example and start replacing some of our operations with these function calls let's do that and then here we'll say is and then down here we can add total and square together and we haven't really made much improvement to our code but it's a little bit of an improvement we've replaced all of our operators with English words that we can read to understand what's going on so we still kind of had the problem of this for loop it was a little bit tricky to remember how to write I would still have a temporary two temporary variables a square variable and this total variable so let's see if there's any array methods there were eight functions in PHP that can help us and of course there are because there is a ton of array functions in PHP and I already kind of mentioned that they're implemented a little bit inconsistently sometimes they're named so kind of strange he exists I don't know how you know that is an array function where all the other ones start with array so instead of trying to look at all of these we're just going to focus on three array math or a filter and a railroad OU's so all these examples are going to use that array that we already saw and those functions that we looked at as well so right map array map allows us to transform an array into another array by applying a function to each item in that array so what that looks like with a ray map it takes a function that we want to apply and in a ray not--they array is the second argument and it will call every value in that array it will call the function for every value in that array and so with our own way rate 1 through 5 if we Square every value we'll get 1 4 9 16 25 just a diagram that help help see visually what's going on each value gets passed down through our square function and then written down on the other side filter works sort of similarly where we're able to apply a function to every value in our array but the difference is it is only going to get a subset of our array rather than the whole because the function that we pass in will be a predicate so predicate is a function that returns either true or false maybe that returns true we're going to keep the value of a returns false it'll get discarded so if we call a red filter on our right which is now the first time you back I'm gonna use his odd then we'll get only the odd values back so I'll get one three and five this for another diagram with how that works each of our values gets filtered through as odd passes and we keep them otherwise we don't all right already reduce so this is the most complicated one to understand so we'll talk about it a little bit longer but the idea behind reduce is that it will reduce all of the values in your down to a single value and that value could be in another array it could be a number it could be a string of bullying it couldn't really be anything but it's the idea that all the values become one in some way so in some other programming languages reduced this called full so that's another way you can can't think of it we're folding all the values on top of one another to produce a new value so array reduce the signature takes an array and that takes a reducer function and then it takes a seat value the key thing to understand with the reduce is the reducer function so reducer function takes two parameters that we're just taking the current item this one has an accumulator program and the current item the accumulator program the first iteration through their array is going to be equal to your seat value but every subsequent iteration the accumulator is going to equal the previous iterations returned and so the best way to understand this is really to look at an illustration of it and so I've shortened it to just 1 2 & 3 for simplicity but let's look what would happen if we're reducing this array 1 2 3 with an ADD reducing question the first time do our accumulator is going to be 0 which is the seed value that we passed in and our current value is 1 we pull that down we're going to add those two together to get 1 that one becomes our accumulator so in our next iteration we have 1 plus our current value of 2 3 3 becomes the accumulator threes also the current value we add those together is 6 we're done iterating so we get 6 so now let's go back to our code and we're going to apply a filter and reduce to do the same calculation that we were going so first we're going to get all of our squares by using our map oh we have two paths on the square first then the alright then we'll get just the odds by using filter the squares with is odd and finally we can get the total by using reduce on our odds with our add function and see that was there so we run this code again we should still get 35 and so again we have these temporary variables and we might want to try something like pass the array down here there could pass this line in to reduce then you get this one long change function call it actually ends up looking worst so instead what we want to do is try to combine all these function calls into another function and that is called array composition or another function composition actually I skipped over that while ago so let's just take a quick look at that definition but function composition is the process of combining two or more functions to produce a new function and if you remember again from math class here we are composing G and F which are functions into a new function that's equivalent of calling f of X and then passing that value into G so G of f of X so we can do that same thing with the functions that we're writing now and we're going to do that using our reduce again and do something called Peck so we're getting there okay as I mentioned earlier functions are first class so they can be used anywhere any other values can be used so what we're going to actually do is have an array of functions and then that will allow us to call reduce on that array so what the pipe function does is it takes a list of functions creates an array we're going to return another function that takes a value and we're going to use that array of functions and it finally will reduce on our array of functions will use the value that you pass in as the seed value and then our reducer function then objects the accumulator the current function in the array and calls that function with the accumulator again it's easier to see what's going on when you look at a diagram so let's set up what we want to look at in the diagram we have some simple functions here add three times two and square if we pipe them all together to create this new function account when we call it with two two will get piped in to add three and then that result get piped in two types too and then that'll get back to the square so in the diagram this looks just like our reduced diagram because pipe is just reducing on an array of functions you see two is our accumulator add three would be a current item we run that to get five that becomes our accumulator and so on all the way down until we get our final 400 so we can go back to our code here and try to use pipe to get these three functions to work together so now let's create a calc function we're going to pipe together these three calls so we'll do a Rain Man with our square and then we'll do our a filter with our is and finally we'll do an array reduce with our add and our seed value now this won't work right because somehow we need to pass in the array to each of these and actually the position of the array that we pass in is different we did see with partial that we were able to lock in a value so we are able to lock in these functions but when we looked at partial before it was only locking in our left most arguments on filter introduced we want to lock in our right arguments so actually have a utility for that partial right looks just like partial except for when we merge our arguments down in this return call the original arguments that you locked in there passing on the right side so we go back here we can make both of these partial right and lock those values then and now if we call Cal on our right we should still get 35 we'll see though yes okay okay so we're getting better here we are able to see exactly how our value our array is flowing through this logic we're going to square it and just get the odds and then have them all together but what we want to do is like we did with our number box we're going to create a box for arrays so I'm actually going to copy over our original box and modify it slightly to accept arrays so now we'll call this array box will keep our constructor the same we're gonna add a new static function call from which is going to take an array just semantics and how these sort of things get named we're from you take you say from when you have a list of things and you say of when you have a single thing so say from this is going to return our new array box and now we'll keep it for fun and call it it'll call from the old box within an array the bigger changes are gonna happen now and map so map is still going to take a function but instead of applying that function to the array that's been boxed in we're actually going to call our array map left our function so let's do that here if they are a map we'll pass in the function and then we'll pass it all right we'll do the same thing for filter introduce those top in case those will change this to filter we have to change the order here and then for reduce we also need a seed value and we changed the order here again okay then weren't able to go back to our example you can get rid of our cow and use our a box instead so alright our a box from our original alright then we'll map using our square function cool filter using her is odd function and it will reduce using our add function and see the value of zero so we did that all correctly we should still get 35 we did good okay so the examples up to this point we're kind of abstract things you would do with numbers things you do as simple arrays where you may not need to go all out and create these more complicated structures or really need to follow the like strict functional programming rules so I want to end with an example that's a little bit more practical where you're going to what we're going to be creating an interface for writing to a file where we're still going to create a class we're still going to use some functional programming principles but be a little bit more practical about it we're going to create a class called violator and it's going to have a private file and to write to a file we need to open it by its name we need to actually write some text to it and then we need to remember to close it which we don't always we're going to do but let's create methods for all those things so we'll say set file that's going to take file name and we'll set our file equal to open we'll open it with the file name and we'll open it in a write mode because we want to write to it create another function that will set the text of the file so that will take a string of the text and then we will do an F right on our file with the texts and finally we'll create a public function to close the file and while you're up close okay this is kind of the standard like object-oriented way that we would create a class to do this so down here in our code what's actually great a new file writer we're going to consume it now to create a to actually create a new file so we need to set the file there's still up test.txt we need to set the text in it I'll just do a hello world and finally we need to remember to close it so that we don't leave the file open the whole time our program is running so we can go down and actually execute this and we'll see that there's 10 CXC and if I write that out of our world next few issues with the file writer API as we have it right now what we're calling new which you said we don't like to do we will always want to call functions - we had a reference the instance of it a bunch of times to pipe all these things together and then finally we had to remember to close it oh no really there's one other things that after you close that it's not clear whether or not you should reuse your app or not could I write another file with the same file writer those all those questions are kind of not answered by this implementation so what we're going to do is change it just slightly to to help alleviate those things so we're going to create a static function called writes and that's going to take a callback function and now look instead of the consumer having to create a new file writer we're going to do it ourselves within the file writer and pass that to the callback function so we'll say is that now the user of this write function can do whatever it wants with that file pass it back to us and then we will close this so they don't need to remember to do that and I can even make this private the other thing that we want to do is make it so that you don't have to keep referencing the file over and over again we do that with a simple shirt to return this from our our methods and so this is similar to how we were turning like a box of every time and of our box examples of this we're not going to create a new instance of our file writer every time we'll just return the same instance you'll make it a little bit nicer to use so now I'll have our file writer it will call right with a callback function and we'll get our instance of the file writer from into our callback and now we're able to do our operations inside of that callback function and we saw really all four of the problems that we had we know I don't have to call new file we no longer have to reference our instance over over we can chain it together and we don't have to remember to close the file or need to worry about if we can reuse this since or not so just by passing I'm a function instead of like creating a new instance ourselves we made this code a lot more readable and so and with that with that example of just how we can use some of the things that we talked about today to make something sort of practical have a better interface that's all thank you I think that was 15 minutes so ice love like ten minutes for questions if people have any other PHP packages that support functional programming yes there are for sure yeah so I would not recommend like creating your own array box or anything I think we're well has a collections class that gives you a bunch of different methods that you can do arrays there's definitely a lot of packages out there too for some of the utility functions like the pipe or partial where you're able to manipulate and change functions thank you
Info
Channel: DrupalCorn
Views: 4,525
Rating: 4.869565 out of 5
Keywords: drupalcorn, drupal, drupalcamp
Id: LZh4_q04aKo
Channel Id: undefined
Length: 51min 33sec (3093 seconds)
Published: Fri Oct 05 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.