An Introduction to Procs, Lambdas and Closures in Ruby

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] welcome to an introduction to procs lambdas and closures in Ruby 40 black typo there gives it a little bit flavor this is really a short side section on my Ruby reloaded course but you may be viewing this in some other way on YouTube or whatever so hello everyone feel free to check out the course that route ruby reloaded comm if you are not part of that course I'm frequently running ruby training courses on there so you might find that interesting so sorry I needed to do a video about was in Ruby what our procs what our lambdas what our closures and how do they all kind of work with each other how do they will relate now I'm really gonna stick to the basics in this video just enough for you to start playing along and to you know hit up deeper tutorials are available out there there's so much stuff out there but I'm gonna look you know a few little tricks along the way so rather than start with a dry definition let's get there with some examples so you should be familiar with the concept of a block in Ruby blocks are essentially nameless functions of a source so let's just give one a while so we get a little bit of a feel for you know what's going on so what i've done here is i've created an array it contains the numbers 1 2 3 & 4 i then have the each method on that array and then i pass that a code block so that's the section here between the do and the end and if i save this and run this you won't be surprised to see the result is one two three four we could also write that in a slightly different way as you're probably aware using curly braces I'll just do it manually here so you can see the process there this is kind of the recommended way of doing it if you're going to do things as a one-liner and as you can see that runs in exactly the same way so we'll I said behind the scenes each is essentially an iterator method that's available on the array class that accepts a code block that's passed to it so we've got our code block here let's print something out and for each element of the array that code block run so that's why we get to see one two three four come out that codeblock is run for each thing for one for two for free for four so it's all being dynamically run multiple times by another method each behind the scenes now this demo already shows off the the idea of blocks as nameless portions of code you can think of them as functions if you wish there can be pasts pretty much anywhere and then which can be run arbitrarily most often in a loop abstraction scenario like this one so we where we've gone over and something a certain amount of times now the interesting leap that we can make here is that we can make our own methods that accept blocks in a very similar way to each and then Dustoff with those blocks so we'll start with the most simple most useless almost example by creating a method in the main context and I have an example ready here so what I've got here is I've got a method called run block and all it does is call a method called yield and we'll get to that in a second and then we have a call to run block and we're passing a code block so again this could be curly braces all on one line but we'll just do it with a doing an end so what's happening well if we just run the code it prints out hello world that's fine what's happening well when we running run block here we're essentially calling the run block method passing it this code block and then yield is another method that essentially yield control to a pass block that's within the current near context but what happens if we don't pass a block so what happens if I just get rid of the block and I run that we get an error an exception of a local jump error no block given on the yield why is this occurring well yield is looking for you know code blocks been passed to the current method it can't find one it fails luckily however there is another method that we can use to check whether a block was given to a method or not and that is called block underscore given query and if I put this it will only run yield if a block was passed so if we run it now pretty much nothing occurs so let's extend their example a little bit now again I have another example here let's add a random underscore each method to the array class so we'll do a bit of a monkey patch here and it'll work a little bit like each like we did in the first example but it will go through an array in a random order so if we jump to this what I've done here is I've opened the array class and then I've created the random each method I've used arrays built-in shuffle method to shuffle up the array into a random order and then using its normal each method and passing it this code block here which then in turn yields to whatever code block has been passed into random each with each element of the shuffled array so what's happening here is I've created an array contains 1 2 3 4 5 and then I call random each two random each I pass along this code block that prints out an element so this code block here ends up being attached a random each essentially once we get into this section here it does the shuffle it loops over and then it will yield to this block here for each element that comes out of that shuffled array so if we run that we get 5 3 4 2 1 it's all in a random order now there's another way that we can write this that will allow us to get a bit of a tighter grip a bit more of control on the code block so let's see I have another example here now this is slightly different in this case we're using a special format here in the list of parameters so normally you'd have a list of your parameters here but then at the end what you can include is you can include an ampersand and in an argument so what this will do is any code block that's being passed along it will assign to this variable so B will essentially become a reference to that code block that's being passed here so within the random each method now at this point when we run this code which as you'll see runs pretty much like the last example B now represents that code block so we're not implicitly calling it with yield or something like that or checking with block given we are now using B explicitly so that's what's happening here we're doing B dot call and then the element that we want to passing as the parameter here so what's going on here what is B we've managed to get an object obviously at this point what is B so we can check this by using the P method to kind of have a little bit of an inspection of B and if we run this we get that comes up here a proc so it's a proc object and we've now pretty much got to the definition of what a proc object is it's essentially a nameless or as it's often called anonymous function or block of code that can be represented as an object and can be passed around and called it will but there is a key difference between props and blocks that we really need to check out and that is that you can only pass a single block to a method at a time so this is pretty much a baked in a restriction in Ruby but what you can do is you can pass multiple procs around you know as parameters to you know in methods because props a regular object so they can be passed around like an array or string whatever you want and there's a little demonstration here it's what we're doing is ignore this method for now we create a proc and see this syntax here we're actually using proc new now to actually create a proc and all the props doing is is just saying you know pretty much hello to the world here this is prop 1 and this is proc 2 so we have two procs and we are calling the run two procs method and we're passing in the props just as if there were any other type of object so if you see here we now have this method it's accepting a mb so it's bringing in prop 1 and proc to sign in them to a and b and then we can literally just do an a call and a B call and everything's pretty much good to go so pretty pretty handy now I need to show off one interesting trick at this point because you may encounter it in your Ruby code that you're looking at those out there essentially if you have a method that's being passed a code block as we saw earlier you can call it with yield we saw that or you can use the the ampersand argument to convert it into a proc which you can run within the method we've seen that as well is however a third way and let's look at that here so this is a very similar to one of the prior examples that we had we have a method called run block and we call run block we pass it a block but instead of you know accepting something in here or using yield you kind of get a little bit of a mixture of the two we haven't actually had to put any specific parameters up here or ampersand parameter or whatever all we've done is we've called prop new and the reason this works is that if you use proc new without giving it a code block to assign to that proc proc new we'll look to see if the current scope has been passed a code block and then it will use that so it will do it all implicitly you know I haven't had to specify anything here the fact is I've not put another code block in here so it's gonna you know pretty much look for that for me and then apply it now if you try to do it in a scope that's not been passed a code block it will raise an error so we can sort of very quickly prove that get rid of this and run this and it says you know we tried to create a proc object without a block so essentially prop dot new wants a code block to use wherever it gets it directly or it kind of implicitly picks one up from you know the current scope the current environment now in ruby 1.9 there are four main ways to call or run a block so let's look at each of them quickly now here we're just defining a proc it has one parameter I and then it just prints out this is my proc and and then puts in whatever was passed to it and we've called it my underscore proc so here's the first way that we can call it we can use the call method we've used this in Prior examples and we're just passing in 10 as the argument here another way and this isn't a typo it might look a little bit like one we use dot parentheses and n arguments and what happens behind the scenes this is kind of an interesting one actually is that that actually gets converted to a call it's basically a synonym for calling dot call it will even work on non projects if they imprint a call method so it's a little bit weird it's you know a little bit of weird syntax trickery going on there but that's another way you can get in another way is to use square brackets and last but not least you can use triple equals and that might seem a little bit weird to you but let's just run this just so you can see that you know he's definitely working we get this is my profit in ten and twenty thirty forty so what's this triple equal thing all about well in Ruby that triple equals operator it's called the case equality operator and its primary function is it gets used with in case statements and them it does have some other uses as well but case statements is pretty much the reason that this came about this syntax specifically for procs anyway so without going into too much detail here's a little explanation a very contrived demo for you now we've created quite a few procs here we've created proxying that have been assigned to variables called several many few couple and none which will give a little bit of a clue about what they they're checking for they each have a single parameter which is number and that comes in and all we do is we just do basically a logical check in all of these cases we check to see is a number above free and less than eight and if it is the several prop you know will return true or false accordingly and we check to see whether it's above free and under eight for the many four few we check to see that the number is exactly free for couple two and none zero and what we do here is we loop around our numbers from 0 up to ten and again we're using a code block here but so we don't need to worry too much about what this code block does because up to is handling that and we get number coming in we say that number items is and then we set up our case statement now hopefully you're familiar with case statements if not this may seem quite confusing to you but what essentially we're doing is we're saying you know query our number at this point and then what normally happens in a case statement is you know if we had something like let's say we had let's say we wanted number five if number is equal to five um now print several so we'd use this kind of construction then what would happen behind the scenes is we would have five equals equals equals number so that might seem a little bit back-to-front but this is what happens in Ruby so it takes each thing goes through the list and then it takes the actual object here does the case equality operator and then whatever it is that is the subject of the case statement so what is happening when we have this type of arrangement win several win many win for you and so on and so forth well we come in and first what it will do is it will take several so I'll create little all that comment here it will use the case equality operator upon it and then it will have number but rather than actually doing any kind of equality check as it would do if we you know had a number or something on those types of lines what happens is it takes the several proc and it calls it passing in number so number makes it to here it checks to see is this true or false if it's true it will run this piece of code if it's false it won't we'll move on to the next thing and so it's really what's going on here now if you have used case statements a lot you might say well we could have just you used a range here or something all those types of lines we could have said you know 4.7 or something along those lines because 4.7 equals equals equals say five would then you know come out to true but we've managed to abstract everything away into proc objects which then being called behind the scenes essentially using that case equality operator by Ruby so this code a clever trick allows you to abstract away interesting bits of functionality and I must admit I've only used this a few times really in any kind of actual live scenario it's not something that I'm always pulling out of the bag but it's really worth knowing about this and it's a nice little trick so let's move on to lambdas what are they well they're essentially just proc objects that you know they are pretty much they are proc objects don't worry about that but they have a couple of different behaviors so this distinguishes them from just plain old procs now we'll start with a simple demo so what we got going on here well this looks a little bit like our system where you know we had a prop dot new and that type of thing have some code in there call it you know I think too surprising well what I can do is I can change that prop dot new to lambda and just as a quick aside actually I don't have to just change the end I could change it to prop and that is actually a shortcut way of pretty much doing the same thing that prop new does so it's a shortcut way of just writing you know essentially the same thing the reason why you might not want to use it is because in ruby 1.8 writing prop will actually create a lambda so it gets a little bit confusing but in 1.9 it's treated you know pretty much the way you would expect but back to lambdas so let's run this code you'll see it's exactly the same way as it did with the prop so what is the difference well one key difference is that lambdas enforce arity what does that mean it essentially means that lambdas are very particular about having the number of the parameters and the number of arguments involved matchup so a lambda expects to be called with as many as it expects to receive I'll just quickly show you a demo because again that is always the best way of showing things off here so what I've got is I've got a lambda we have you know free parameters in here ie B and C but when we call it we're not actually passing in anything so there's nothing for a B and C to receive so if we just run this it says wrong number of arguments zero for free so it's a lot like a method in that respect you know it's not going to sort of stand for any nonsense in that regard whereas if I change this and remember this is 1.9 the way we're doing this we're change it to prop in this case it works fine and what's happening in this case is that a B and C will be set to nil because there's nothing for them to grab so they'll just default to nil whereas a lambda acts a little bit more like a method with its parameters and it will just give up and throw that exception so another key difference is in lambdas and props return semantics so again you know another really quick demo what I've got here is I've created a single method called run a proc accepts a proc comes in as P and what we do is we say littering prints of the screen at this point we're going to start running a proc we call the proc and then we say we finish running the proc so if we break this up a little bit the first thing that we do is we call run a proc and we pass in a lambda that prints itself it will say I'm a lambda and then there's a return so a bit like you would do in a method we have a return within a lambda here and then what we have following up with that is we have exactly the same thing but with a proc instead of a lambda so this is a plain proc object now if we run this with the lambda we make it into run a proc it says starting to run the proc it says I'm a lambda and then it's just finished running the proc when we use the normal prop however that the bare-bones proc it will say starting to run a proc I'm a proc and then whoa exception unexpected return what's going on here well the reason for this is that a return in a proc will try to do a return from the context of where the prop was defined so not where it's currently running where it's actually defined so since that's in the main context so we've actually defined the prop here this return is trying to return from our scope out here which is the main context and you can't return from the main context so we have a big issue on our hands but we can tweak this to actually you know reach the end of the program so what we'll do is move on to this what I've done is this is really really simple way of doing it I've created another method called our program which I call later on so this is just really wrapping this up very simply we again do exactly the same thing run the lambda run the proc and if we run this you get to see the lambda in runs out fine the proc in says I'm a proc but we never see finished running the proc and the reason for that is that when we get to this point we pass the proc makes into here we print out starting to run a prog P dot call but because it's doing a return procs return from where they are defined that context where they defined which is here so at this very point when we've done this call it's returning from here back out and exits our program so that's why we never get to this line here now I just have to do one little change here that will really show this off for you so we'll just quickly do a little test of this so we'll switch around the proc on the lambda this will really show you the kind of how far this technique goes we'll run this one and all it does this time it starts to run the proc I'm a proc and we see no more we don't even see the lambda or anything like that at all so literally at this point it's run this it's pushed in this proc up to here calls it the return drags us right out of our program and then back down to the bottom here where the program terminates now at this stage before we move on to closures which is that the next thing we're gonna deal with I just want to recommend to you that you check out a page in the Ruby documentation so all you need to do is search for Ruby proc and Google that the first result is usually at the Ruby doc site and it will show you documentation for the prop class and it has some pretty interesting methods on there now remember that procs do include lambdas even though they are just a slightly different flavor of procs and there are a few interesting methods there including lamda query which allows you to ask a prop are you a lambda or not that may be useful to you and also source location is one of my favorites which tells you which file and where your what line and everything a prop was defined and this can come in really handy if you wanna make your own little testing library things like that and if you were on the Ruby reloaded course you would have seen how I put together the test rocket testing library that I developed and I pretty much use that the source location of effort to good effect so closures what are they well in Ruby closures very much like what you've seen already so you take something like an anonymous code block or function something this you know in a proc object but one that maintains references to local variables that were present and being used at the time of the definition of that code so as we have everything else demonstration is probably just what we need here so this is quite simple what we've done is we've got a local variable called name and we make it point a lovely string object that contains the name Fred and then we have another variable here printer name which is associated with this proc object that all it does is it just prints out name to the screen and then we have you seen this a few times a run prop method that just accepts a prop as you know as an argument comes in and runs that runs out with the call off we go well you know may think yeah this will work fine we'll just run this it'll print Fred outs of the screen but if you think about it's really logically named exists here it doesn't exist in this scope here so when we're doing this puts name something magical must be happening so that when this prop makes it into P and then it's being called that magically makes name kind of come back to life and we can access it once again that kind of magic is called a closure so what's actually going on here well as you can see once we define name we do the puts name name is treated put into the closure it can tell that you know we're actually using name within that prop and that's why all kind of gets boiled in and that's why it will you know hang around as part of the prop means it can be used later so let's just look at a slight a slightly more interesting example which shows how we can use a closure to dynamically define function behavior so are quite like this one this is a common example you'll see in a lot of books so what I've done here is I've created a method it's called multiple generator and this is a method that will actually create a lambda object for us and what we do is all you just do is pass in a number such a multiplier it creates a lambda which in turn accepts a number it multiplies that number by the multiple that's coming through N and that's it very simple so what I've done is I've created a doubler and a Tripler so all you do is you're call multiple generator with two and three and what that will do in turn is it will create a lambda that accepts a number and then takes that number multiplies it by 2 in this case or by free in this case so what's actually happening is when we're calling multiple generator 2m comes in exist so your m is equal to two at this point and then M is stored within the closure of this lambda so then that lambda can then be use later on as it is here we've put double ax five that could be coal but we just use the square brackets for interests a little bit of a change here and then what that would do is it will print out ten and if we take the Tripler because free is now M we get 30 so if we run that pretty much as expected now Ruby's closures have an interesting behavior compared to some other languages which some other languages their closures will be implemented with a data structure that stores the variables and their values at the time the closure is created but in Ruby merely a reference is kept to those variables so it means that the contents of those variables can be changed before the prop gets a chance to run so let's jump back to a copy of a prior example and see that in action so this is a lot like the example that we did just a minute ago where we you know take a name we print out that name in a proc but then what I've done is I've changed what name is equal to at this point from Fred to John so you may think well okay at this point the closure was formed when we defined the proc so you know it might set name and it'll be Fred for evermore so once we do Peter call it will print Fred but because of what I've just explained about it keeps a reference to the variable rather than the value over the app with very time what happens is we run this and we get John so in this case name became part of the capture but because the value then changed Andrew bequite a reference the updated value is recognized as names value in the closure at the time it's called as well now obviously a lot more to closure to this but that is the very basic introduction as to what they're all about and if you are part of the Ruby reloaded course or actually even if you're watching this on youtube nor leave a comment or something feel free to say if you want to see a slightly more advanced video digging into a specific thing I really wanted to just provide you with a basic vocabulary here and the basic example so that now when you encounter these sort of things you understand roughly what's going on rather than perhaps looking at some of the weird edge cases that you almost never bump into in production code so this will get you quite a long way but if you are part of the course or whatever coming to the forum request a video more than happy to produce it for you so that's it for now a very basic introduction to blocks procs and closures I hope you enjoyed it
Info
Channel: Peter Cooper
Views: 42,932
Rating: undefined out of 5
Keywords: ruby, proc, lambda, closures, lambdas, procs
Id: VBC-G6hahWA
Channel Id: undefined
Length: 27min 12sec (1632 seconds)
Published: Fri Dec 09 2011
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.