Code Blocks in Ruby

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
so now let's talk about blocks blocks are used all over the place in Ruby they're so ubiquitous that a lot of people don't even know they're using them when they learn Ruby they're just following the example and they don't know that every time they say the word do they're creating a block and passing it in to a method but that's what's happening a block is a chunk of code the concept of blocks overlaps it's not entirely identical with but it overlaps with the following set of terms there are lots of ways of doing the same thing which is taking a bunch of code and instead of executing it immediately making it in a little capsule of code and then letting someone else call it a little later so some languages do this with something called function pointers some languages do this with runnable x' like Java you can't actually yet have a closure in Java and you can't get a proper pointer to a function in Java so you instead you make an object that has a method named run and that's a runnable right in JavaScript a function is a function pointer really it's an object but it's also sort of a pointer to itself if you define a function you can then just set a variable to point to that function and that allows you to call it later if you want other terms proc lamda callback function thing called delegates and those are a lot like function pointers all right so the basic idea is it's a chunk of code that you're not going to execute right now you're gonna execute it a little later or maybe a lot later or maybe never another term for what this is doing is it is ray of fiying code - ray Fi does that mean to like send ray beams out of its eyes - ray fi means to make it into a thing it comes from the latin term race which means thing r es right thing - thing of phi it's making code into a thing ruby has three different well several different concepts that are very closely related but distinct a function which I should really call a method but Ruby doesn't have actual functions so whenever you say function and Ruby really mean method a method is a chunk of code that starts with death that gets attached to a class and executed by a method dispatching a block is a chunk of code that begins with do that gets passed into another method and we're gonna see exactly how in just a minute a proc is actually an object that contains a block so if you want to get a pointer to a block put it into a variable you can't do that directly in Ruby you have to make a block and then make a proc out of the block and then point to the proc it's a little convoluted I'm going to show you exactly how that works in the net starting in the next slide but just to I just wanted to throw these terms out here there's a subtle but important distinctions between methods blocks and procs a proc is an object that points to a block a block is a chunk of code and a method is also a chunk of code but with completely different calling semantics yes a block starts with do curly braces are sort of shorthand perdu right and it's I don't like the curly braces it would be better the curly braces didn't exist because curly braces are also used for hashes so it's a little ambiguity in the language that's irritating so every time you see do as you just has you just previewed every time you see do the start of a block so fix num has a method called x and x execute that block this fixed num number of times so if I'm if I want to execute this code here they're right I'm executing this I'm taking this line of code puts hip-hip-hooray I am making it into a block reifying it and then x is calling that three times in a row notice that at the moment I declare it nothing is happening it's the x method that is calling back and executing that code so yes blocks can be wrapped in curly braces a lot of people who are new to ruby prefer the curly braces because it just looks more like Java or C or JavaScript get over it use do end rather than curly braces for the reason I just said which is that with the curly braces there's some ambiguity with parsing because it could be a hash and that leads to some subtle annoying syntax errors sometimes indentation is also yeah it's well depending on your editor it technically there's no difference it's just a symbol like do vs curly brace it's just a symbol so you should be able to figure it out it's just it's better to use do act um we're gonna see in just a few more slides it's not actually a parameter or technically it's not a named parameter it's an anonymous parameter and we're gonna see what that means in just a minute but yes it is being passed in two times just not in the normal way so blocks are used for lots of different purposes they're used to solve lots of different problems the idea is that you leave it up to the receiver of the block to decide what to do with that code and when or whether to call it so one thing that blocks are used for is callbacks let's say that you want to ask your networking library to read in a TCP connection and then as soon as that connection is closed take the contents of that the entire can the entire packet of stuff that got passed in by your network client and pass that into a block right so you're gonna call it later at a specific time but you don't want your code to wait for that to happen because it's IO bound it might take seconds and seconds you know which is like eons to a computer you don't want to wait for that IO to come so you make a block and you pass that block in and then you trust the library to call that block later when that data is available all right you can also use blocks for initializers lazy initializes let's say that I'm writing Sinatra app Sinatra is a web app framework that lets you set variables maybe at the time that you declare your program you don't know what the proper value is so you make a block that will be called later and you that block will know what the proper value is at the time that it is called alright so it's for initializing things but initializing them later on not like when you declare the program but when you're running the program so some more stuff has been set up in the meantime they're also used for iterators which we've been using a lot they're used for separating the content the body of an algorithm from the boilerplate around that algorithm loops are of the classic example the iterators keep track of initializing and index incrementing the index grabbing the value and in the case of map collecting the results into a new collection and then after having done all that standard stuff they call your block and your block does the specific stuff the idiosyncratic stuff that you're particularly will need so it extracts common boilerplate stuff inside of a method and then calls back in that case just like with x it happens immediately but it's still a little bit later and it still makes sense to do it it's not a callback in the sense of waiting for something to happen long in the future it happens immediately into the future but it's still into the future it's also useful for testing like the before block in our spec right you're specifying a bit of code that's going to be run before every single examples back single it block by the way the it block is also a do block it's also uh you know turns into a prop also it just looks cool sometimes your code just looks better if you do things with blocks rather than doing things in the exploded C style that's another great example if you didn't hear the sort routine has a standard comparison which is to use the Flying Saucer but if you want you can pass in a customized sort block right the sort block would take two parameters the left side on the right side and then return you know negative one zero or positive one but you can do whatever comparison you want inside that block so when you say area dot sort do you're configuring the algorithm the way that you want it so let's talk about the proc keyword crop turns a block into a variable or rather into a reference and then you can store that reference into a variable so in this case here if I want to say proc do right in this case it's on one line so we're using brace but if I say proc do puts high that has made a reference to an object that when you call that object it will execute that block and by call I mean say object call so in this case I've put it into a variable called say hi and then I say say hi call and that will execute this block and of course procs can take parameters right blocks can take parameters and therefore proc all passes its parameters into the block yes question yeah yeah and I've been it's a little technical but what's happening here is proc is actually a method or it acts like a method and then this is the block so this if this is du puts high end that defines a block proc turns that into a reference and then you'd say reference call that executes the block again so when you say call call this passes its arguments directly into the block make sense okay so if I wanted to write a version of x that wasn't as cool sort of a dumbed down version of time I could do it this way I have a method called twice do that takes a proc which i'm naming action could name it anything I'm just calling it action right now and inside of that method I'm calling it twice so now when I say twice do well actually let me let me spell it out I now have a variable say hi and that variable as you see the value of that is actually a proc so if I wanted to say say hi dot call it does it sting or I could pass that in as a named parameter and then twice do is going to call it twice right now I don't need to put it into a variable if I don't want to I could do that all in one go like this twice do croc do puts high end right so this thing here defines a proc and passes it in what's the proc gonna do well it's gonna do this it's gonna do whatever I said and do it all right so now here comes the kicker the default block should have been called the anonymous block or the invisible block but it's called the default block it is invisible and it gets passed in just like this guy got passed in except instead of being passed into a named parameter it gets passed into an unnamed invisible anonymous magic parameter and on the inside of the method in order to call it instead of saying action about call you say yield so if I wanted to do the thing I just did with crops but using the default block instead this is the implementation of that method and now you see why I put that weird underscore do in the previous method name because now I can say twice do put some up sorry puts did that work yeah let me try that again twice do puts high n so this here is equivalent to this here but this second version is much more concise and it reads moral English right it's got that domain-specific language thing going on here I can actually say twice do this yeah it is actually a block I if I had said the word proc here that would turn it into a named parameter and explicit parameter but because I didn't it turned into an implicit parameter and this is something that's magic and built into Ruby now I want to point something out this is not the only magic invisible parameter there's also one called self all object-oriented languages most object-oriented languages have a variable called this or self and that's actually not a global that's a parameter it gets passed in to every function call so that that function knows which object it's it's attached to right now the same thing happens here it's another invisibly passed in parameter to every single method most of the time it's blank but when you actually specify it the value is filled in the only difference is between self and this guy is that this guy doesn't even have a name it's not even called self it's just called nothing it's called the default block it's anonymous self at least has a name my name is self but this guy doesn't okay question and we're gonna see that in a minute not right now but in a minute so I just want to belabor this point forgive me for belaboring the point but I just want to point it out Ruby could have worked perfectly well without invisible blocks it could have worked just fine except then it would look like this Matz decided that this code down here was prettier than this code up here it's more concise and arguably it's more clear and that's the only reason why we have this weird magic you know plumbing of the default block which makes an anonymous parameter passed into every single method and this weird call yield which really means default block all right it's just to make the language look nicer and there are other reasons for that as well like there's there are certain ways in which we've now crossed some magic threshold where we can make it we can make our code look really super nice instead of just a little nicer but right this here twice do puts high this allows us to make a method in this case twice that looks for all intents and purposes like it's a keyword in the language so for instance if right if it's a keyword in the language while is a keyword in the language and if you say while condition blah blah blah end in this case I say twice do blah blah blah and it just looks like it's part of the language it lets the language grow right if this is metaprogramming it makes it it just makes the syntax for methods approach the syntax for keywords alright that's that's the reason for it you might think that's a dumb thing to do but that's why they did okay question yeah mm-hmm no not at all I could name it twice but then I couldn't execute it in the same window that's all I could have named this step twice totally fine I'll do that right now if you want all right so now I can say twice proc do puts hi and I messed up I need to see this is why the syntax gets a little weird right with parentheses and stuff if you don't follow the rules but anyway there that was just part of the name the underscore do is irrelevant yeah when you let me put it this way when you call yield if there wasn't a default block it's a runtime error right so like right now it's like that's called yield it'll say no block given because I'm just in pardon uh-huh oh well this I lost the original place but yes twice nothing no bucket so if you're gonna call yield you better make sure that there's a block I mean sorry a default block so of course the default block can accept parameters just like any other block can so here I say do with vertical bars rather than parentheses so here this is a block that takes a parameter and then prints that parameter with the string Mississippi so here I'm saying yield with zero and then yield with one and that way when I call this this guy gets a zero prints it has one to it because we want to say one-mississippi two-mississippi not zero Mississippi one Mississippi alright so here I'm yielding zero that just means I'm calling the default block with a parameter zero and let me show you a more advanced example of blocks that take parameters here I'm reimplemented each and again this for underscore that's just my decision for the name of it I didn't want to call it each because we've already got a method called Honore but this one takes an array so here we have a method that takes a named parameter and a nameless parameter so when I call it I say for each passing in the array and then I'm also passing in a block notice another little syntax weirdness the dew has to be outside of the parentheses if you have parentheses it's like a whole separate parameter list that can only have one item in it all right so just going through this in a little more detail this iterator initializes an index says while that index is less than the size of the array grab that item pass it in as a parameter to this block and then increment the iterator and then when we're done return self because that's what each does not sell in this case it's the array right returning the original collection is what each does any questions on this slide okay so blocks can also return values just like a regular old method they can accept parameters they can print stuff they can do calculations and they can return so here's an example of map I'm reimplemented map on the outside of an array rather than on the inside of an Iraq so let's look at how this works I think I initialize the index and I make an array that starts out empty and then for each item inside of it I'm going to yield that item I free tied them inside the original array I yield that item and then I take the return value and append it to the output array and then at the end I return the output array so in here this works just like the regular map except instead of names map its map it names this block accepts an item and reverses it and returns it so this works just like a radon map except it's map of array questions on this okay let me let me ask how many people understand this okay everybody's got to go in like this that's all I can expect for 1225 in the morning or no in the afternoon in the morning would be give you another time okay let me move on this answers the implicit question that were at before which is what if you didn't get past a block in the spirit of duck typing usually you should just fail if you're supposed to be passed in a block and the caller didn't pass in a block you should let it fail fail fast drag fail early fail often is the the motto of a Ruby programmer right you want the runtime errors to happen because that's kind of all you've got it helps the programmer right it helps the caller if the receiver fails with bad data you don't want to protect the caller from himself so in this case I'm assuming not the normal case the case in which the method works with with a block passed in but it also works without a block passed in so in that scenario I can call a method asking whether the block was passed in and that method is called block given and usually that's used in code like this instead of just yield its yield if the block was given again this is not the standard usage of yield you should have a reason for calling block given and that reason should be that you know your method should work with a block or without a block it's a decision an actual proactive design decision to use block given it's not the standard okay here's some relatively advanced stuff sometimes you want to write a method that takes in a default block but then saves that block away for later use in that case you have to turn a block into a proc on the inside of a method remember you can't actually store a block inside of a variable you can only store a block in a proc and then put the proc in a variable so this is what does that if you say ampersand and then name a variable that's just a magic incantation that says take the default block make a proc for it and put it in the variable name P so later on I can store P and an instance variable or just say P comm remember it's a proc so you say prop call if it's just a block then you say yield the little disorienting I know I'm sorry I see a couple of people's heads exploding sorry this won't go on for much longer maybe we'll pick it up again after lunch but this is just the ampersand turns the default block into a proc you can also turn a proc into the default block on the other side right if you're a caller and you're calling a method that takes a default block but you've got a maybe it's something you've stashed away from long ago into a variable you can turn that into the default block by using the same ampersand it means the opposite on the outside it means turn a proc into the default block on the inside it means turn the default block into a proc same sense but it's just completely the opposite okay now I promise this is the last slide before lunch I've been using the word proc all over the place proc and block Ruby has actually two types of closures ruby has profs but they also have lambdas procs and lambdas are almost the same thing with two very important but very subtle and technical differences the first difference is that in a lambda it cares about the number of parameters that you pass in but in the proc it doesn't care so let's say that there's a proc if you call the proc with ten parameters but the proc was just defined do blah blah blah and with no parameters that's fine but if it was a lambda then it would care it would actually say wrong number of parameters just like a method that's the other difference is even more subtle which is the behavior of return if you use the term of return inside of a block if that block is in a proc then that return statement returns from the method that is calling it but if it's an a lambda it returns just from the block and that's fact there just means never use return ever in your life ever because it's too confusing because you're just gonna you're just gonna mess it up at some point in your life if you ever use return so just don't do it just use if statements and then end at the very last line seriously trust me just go I mean maybe okay in a year you can start using return again but that's that's one of the reasons why I recommend not using return at all because you might be inside of a block and in that case return would return from the method or you might be inside of a lambda and in that case it would just return from the do n block and it just the fact that you might not know what's going on is just part I see you're looking at him so he's been using your turn alot huh okay anyway so now you might have enough information to do these labs I have one more hint which is the silly blocks Labs are silly they don't make any sense at all they're really dumb there's no reason why any real programmer would ever write functions that they have that way the problem is it's really hard to write tests for teaching blocks so let me just give you a hint about the very first one there's a method called reverser you need to write the method reverser that method takes in a block that block returns a string your method needs to call that block and then reverse the strength and then return that so it's weird because there's just so many things going on so many moving parts right the test calls your method with a block your method calls that block which oh by the way it's back in the test does some stuff returns it back to your code and then you do more stuff to it and then return it back to the test it's just insane so hopefully I've made it a little less insane by jumping around and waving my hands okay everybody any more questions before we go to lunch okay thank you
Info
Channel: Alex Chaffee
Views: 10,554
Rating: undefined out of 5
Keywords: ruby, blocks, functions, lambdas, yield
Id: K8uhD8mtorE
Channel Id: undefined
Length: 30min 16sec (1816 seconds)
Published: Fri Aug 24 2012
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.