 With those with those with those who are already on the show, I want to give you a preview of what Llanelli is rolling out of your story. Hello, everybody! I guess in some ways the premise of my talk is kind of similar to the last talk you've just seen. When fun with Lambdas, you saw Corey restrict Ruby to use a subset of the functionality and look at what this did to the language. I'm going to do a similar thing. I'm going to restrict Ruby to be a subset of this functionality and see what it means for the way we can use it. I'm going to turn Ruby into a pure OO version. Now, the inspiration for this came from another talk you've seen in this conference. It was Sandy Metz's excellent Nothing Is Something talk. I saw this talk about six months ago at a conference called Bath Ruby, in the English town of Bath. I was struck really on by this slide. This is the point where Sandy says that the if keyword is not necessary in Ruby. Now, when I saw this, I thought, this is really weird what's going on. When I think about languages with small syntax, I don't think about small talk. Maybe I should do, but I don't. I think about list languages and, I guess, in particular, scheme. Now, if you try and write a list language, you need to get lists working, but after that, there's only four things you need to get working. You need to implement these four basic functions. Now, the syntax for scheme is much bigger, but it can all be built out of these four fundamental ideas. Of course, one of these is if. When I saw Sandy's talk, I was thinking, well, I think if this is fundamental for the way programming languages work, so how can you take it out of a language? It's a shame I was thinking this rather than paying attention to the talk, where Sandy was explaining how it works. I had to go back afterwards and look at the video. I forgot about this after a while. There were much bigger ideas in Sandy's talk, and I didn't think about this anymore, until a few weeks later, when I was creating some content for a company called Pluralsight. Pluralsight do training videos for developers and other technologies. I'm creating a course for them called Ruby Beyond the Basics, and part of this course is looking at Ruby's object-oriented model. In particular, I was creating a video that looked at what we might call the primitive objects in Ruby. These are things like numbers and strings and arrays that come built into the language. We often say that these things are objects, and they do behave like objects, and we can send messages to them, but they're special kinds of objects, and they're special because they've got a special local state. To understand this, let's look at an example. Let's imagine we're creating some kind of object model for this conference. We might start by creating a speaker object, and here I've given the speaker a single attribute of age. For some reason, we want some of the age of the speakers. Age is just an attribute inside Ruby, and this is what I'm going to call the local state of this object. When we instantiate this object, let's say we create a speaker to represent me, we're going to set the age to something, so we'll set it to 21, because 21 is clearly my age, right? When I say we set the value of this age attribute, what I really mean is we create a reference to another object, and in this case, the reference is to a fixed num object. We say fixed num is an object, and it's got this local state of 21, right? But this can't be a reference to another object. If it was, it would refer to another fixed num, and this would repeat forever. There's something very special about the local state of fixed nums. Now, when I was recording the video for this, I said something along the lines of, you can't have pure OO numbers, and I think this is a really interesting observation. It's also completely wrong, and I knew it was wrong as soon as I said it, and I had to go back and re-record the video to ignore this issue. And this reminded me of Sunday Met's talk. I thought, you know, I thought if this fundamental language, it turns out you can take it out, and I thought you have to have, you can't have pure OO numbers, you've got to have some special representation, and that's not true, either. So I got kind of interested in how far we can take this idea, how far can we go towards creating a pure OO version of Ruby? Now, before I start looking at this, I just want to set some ground rules, because we all kind of understand what OO means, but we might have slightly different interpretations of it. So I'm going to define OO in terms of four simple rules. There's only four things we can do here. Now, first of all, we can define objects. It wouldn't be much of an OO language if we didn't have objects. And when they say define objects, what they really mean is in Ruby, we can create classes and we can add methods to those things. Once we define objects, then we can instantiate them. And we do this by sending the message new to our class. I guess more specifically, we send allocate. It creates some memory and then it remembers which class it belongs to, so it knows which behaviour it has. Once we instantiate objects, well, we can remember them, we can store references to them. And in particular, object can store references to other objects. And then finally, we can send messages to our objects. And of course, our objects can send messages between each other. Now, when I said we can define objects, it's important to note that this has to follow the same rules. Okay, so when we're writing a method, we have to follow these rules. We can't just do whatever we want. We can only instantiate things, we can remember where they are and we can send them messages. And nothing else is allowed. This actually cuts out an awful lot of Ruby. So for example, we can't use the built-in classes like fixed num and string and array and so on, because these don't meet the rules. We also can't use nil, although to be honest, this is something of an improvement to Ruby. We can't use the built-in control flow structures like ace and case and logical hands and ores. And in fact, we can't use true and false because these also don't follow our rules. We can't use blocks or procs or landers. And this is really annoying because we can't use these handy methods like each and map and inject and so on. We can't use Ruby's built-in notion of equality because this doesn't follow our OO rules. We can't use puts and in fact, we can't use any IO structures at all because again, this doesn't follow our rules. And all this taken together means we can't use kernel. Pretty much everything in kernel will break our rules. And indeed, most of the standard library in Ruby will also break these rules. So can I still use Ruby? Well, yes, I can. All of the code you see is going to be Ruby code. It just might not be the kind of Ruby code you normally write. So let's get started and let's start with numbers because this is where I began. So how do you build a pure OO version of numbers? Well, there's not much we can do. Our rules are quite restrictive. But one of the things we can do is we can define objects. So we can start doing that. We might say, okay, we need the number zero. So we'll create a class for that. And then we might say, okay, we need the number one. We'll create another class for that. And then two and three and four and so on. You can just imagine it's continuing forever. So this is kind of good because it follows our rules. But it also kind of sucks. And there are two big problems with this. The first is that there are an infinite number of numbers. So I want to build a number system this way. I would have to create an infinite number of classes. And that would make the most boring talk you've ever seen. But even if we could solve that, even if we could all agree that we'll just use the first 10 numbers, we still have a big problem because these are not numbers. These classes have got the same English name that we normally apply to some numbers. But that doesn't make them numbers. They don't act like numbers. There's nothing numbers about these things. So this approach doesn't work. So then how do we solve pure old numbers? Well, it helps to step back a bit from the problem and think about what it is we're really trying to model. So I've written out some numbers here, the number zero to four. And you can just imagine the sequence continuing forever. Now, when I create some classes like zero and one and two and so on, I treated these things as discrete objects. There's no relationship between them. But that's not how numbers work. There was a relationship here. In fact, these things are in a sequence. And this is kind of handy. This means that we have the notion of zero. We can use that to give a definition of one. We can just say that one is the number that comes after zero. And this works for other numbers. For example, two, we can say two is the number that comes after the number that comes after zero. And this works for all of our numbers forever more. This turns out to be a surprisingly powerful concept. So let's look at how we write this in Ruby. Okay, so I said we got zero. So I'm just going to create a class for that and I'm going to call it number zero. And for now, I'm not going to do much. I'm just going to add an inspect method so you can see what's going on. Okay, and inside here, I'll just print out string zero and that's it. Okay, now I inspect and the string that I give back are not actually part of my pure old language. I'm just putting them in here so you can see what's going on so you can follow along. It means I can write things like number zero and create that and then run the code. And you can see it prints out zero. It's just a utility thing. I could remove all the inspects from the code at the end and the code would still work. It just means that you can't follow along. Okay, so that's zero. What about other numbers? I'm going to use a single class for that. Okay, I'm just going to call it number. Now, numbers are a bit different. When I was talking about numbers like one and two and so on, I always said they come after something like one comes after zero, two comes after one and so on. So when I create these objects in the initialiser, I'm going to pass in the thing that they come after. I'm going to call this predecessor and store it in, and that should be called pred for now. That's short. And then I'm going to inspect with them here so you can see what's going on. And in here all I'm going to do is inspect our predecessor. And then on the end of that, I'll just put a little dash so that you can see that this thing is here as well. Okay, so we have some definitions. Now let's create some numbers. So we can say zero is a new instance of the number zero class. Okay, and then we can say one is the number that comes after zero. And like I was going to say two is the number that comes after one. I run the code and you can see this works. You can see it for one. It prints out zero with its predecessor, then one dash. And for two it prints out the predecessor and then two dashes. So this is kind of cool. This idea of being the number that comes after is something I use quite a lot. So I'm going to create a helper method for this. Okay, it's called successor or suck for short. So I'm just going to add a method inside here called suck. Doesn't take any arguments. And all this does is returns its successor and the successor is the number that comes after this one. And we do that by saying number.new. Let me just pass self in here. Okay, and I'm going to add the same method to the other numbers as well. So like the successor one is just the thing that comes after one. So it's going to have the same definition. And then down here in our code, our definition of zero stays the same, but we can now use a shorter version for the other numbers so we can say one is the successor to zero. And likewise, we can say two is the successor to one. I can keep going with this as far as we want. So we can say three is the successor to two. And four is the successor to three. And then on the code, you can see it works. We have ways of creating three and four and so on. And this is already kind of cool. This means I've got two classes I'm done here and we can create this infinite sequence of numbers. That's pretty good. It's solved one of our problems. But it still doesn't really act like a number. We can't say this is a number yet. This is just an infinite sequence. So we need to add some behavior. And I'm going to start with addition. Addition is a kind of fundamental concept with numbers. So how do we add two numbers together in a pure older way? This is kind of a hard problem. Maybe it's too hard to solve in one go. One of the things I like to do with a really difficult problem like this is find the single simplest instance of that problem that I can find and then solve that and see what I learn. Now, the simplest problem to an addition I can think of is adding zero onto things. Okay, so we can create something that says, for example, zero plus zero. And we would expect that to give us back the answer zero. We don't know how to add numbers together. And I can say zero plus one and I can expect that to give me back the value one. And I can say zero plus two and I'd expect that to give me back the value two. In fact, zero plus anything just give me back that other thing. It doesn't modify it in any way at all. Now, not modifying a thing sounds pretty easy to do so I can just go and implement that. I'll add a method up here to zero called plus. It takes another number and then just returns it. It's just the identity function. It doesn't do anything to it. Go back to the code and run it and you can see that this works. Zero plus zero is indeed zero. Zero plus one is indeed one. And zero plus two is indeed two. So that's great. What about other numbers? What about say one plus two? Or maybe two plus two. How do these work? Well, we can't use this in trick. We can't just say we'll return the other number because that's wrong. One plus two is not equal to two. We can maybe think, okay, one plus two is equal to three. That's a successor to two. So maybe it's called successor but that doesn't work for two plus two. So how do we solve this? Well, again, it helps to stop. Take a step back and think about what it is we're doing. So here's the same thing as our original numbers. So here we're trying to add one onto two and I don't know how to do this yet. This is pretty hard. Do you know something about the top line here? We've got the number one and I know something about the number one. I know it's the number that comes after zero. In fact, if we look at the predecessor of one, we would end up with this addition zero plus two. Now, the nice thing about zero plus two is, I know how to solve this. I know this gives me back the answer two. We've just seen this. That's kind of cool but it's the wrong answer. But it's the wrong answer because I had to take a step back towards zero to get it to work. So I'm going to balance that out by taking a step away from zero to get the final answer. That is, I'm going to call the successor on the answer it gave me back. And that gives me three, which is pretty good because that's the correct answer to one plus two. Okay, so that's one. What about adding two onto two? Well, again, I don't know how to do this yet, but I can use the same trick. I can say, what's the number that comes before two and this is the number one? And that's great because that gives us one plus two and we've just solved this. We know how to do this. This gives us back the answer three. Now, we need to balance this out again. We took a step back towards zero so we're going to balance that by taking a step away by calling the successor. That gives us four, which is the correct answer. And in fact, this works in general for adding positive integers together. So it's going to add it to the code. You go down to our number class down here. I can add a new method called plus, takes another number and all we need to do is add it onto our predecessor. Whoops. And then call the successor on the answer. Okay, we go down, run the code, say it in action. You can see that this works. So we can add one onto two and we can add two onto two. Now, let's just stop for a second and look at this. All I'm doing is defining classes, instantiating them, remembering where they are and sending them messages and yet just using this, we can rebuild a number system and we can make them add together. I think that's kind of amazing. There are other behaviors of numbers we might care about, things like multiplication, subtraction, so on. I don't have time to define them all here today but they all work in a similar kind of way. Subtraction is a little bit tricky but they all kind of work in this way and we can rebuild a number system. There is one other behavior I want to look at before I move on though and this is the idea of equality. So I expect people to say something like, zero is equal to zero and that should come back and say, yeah, that's fine, that's true. But if I say something like, well, zero is equal to one, I'll expect that to come back and say, no, that's wrong, that's false. Now, a little bit of a problem. I don't actually have true and false yet I need to go and build those things. So I'm just going to quickly go to another file and define them. We're going to come back to these in a moment but for now, all I'm going to do is create a class called true. It's not going to have any behavior yet, it's just a placeholder for what it means to be true that we'll come back to later. For now I'll just add the inspect method so we can see what's going on. I'll get the print out true. I'll do the same thing for false. I'll add an inspect method in here, I'll get the print out false. Okay, and that's all I'm going to do for now. You go back to our numbers file I'm just going to pull this class in so this was in a file called booleans. So we can use it here. And then we can look at the idea of equality. I'm going to start with zero again, zero is quite an easy thing to start with usually. So let's have the method called equals equals. Let's take some other number. How do we know if something is equal to zero? Well, it's kind of easy. If the other thing is also zero then we know the two things are equal. So how do we know the other thing is zero? It might be kind of tempting to think well we can look at the class but looking at the class is not one of my four rules. We can't do that. It turns out it is even easier than that. To know if something is zero we just go and ask it. We say R U zero. So we can say to the number R U zero. And then we return whatever that gives back to us. Now I obviously need to implement this method. So for our number zero class we can say R U zero and the answer is trivially yes. So it returns a new instance of the true object. Now for other numbers, numbers that are not zero the answer is obviously going to be false. So we can just add a method to here called zero. It doesn't take any arguments. Just return to the instance of false. You go down here and run our code we can see it works. Zero is indeed equal to zero and zero is not equal to one. That's pretty cool. Let's look at the other numbers. So I should be able to say things like one is equal to one and that would come back and say true. But if I say something like one is equal to two well I expect that to come back and say no that's false. Okay, how do we compare numbers? Well we can just say are you non zero because that would make one equal to two that would be the wrong answer. But there's something interesting about looking at the quality of numbers. I can say that if two numbers are equal then I also know that their predecessors are equal. So if I say is one equal to one I look at the predecessors both sides I say is the predecessors one equal to the predecessors one which is is zero equal to zero and we know that this works so we can see the code above. And likewise for one equal to two well we same thing we look at the predecessors we say is zero equal to one and we know this comes back as false. So actually equal is pretty simple. If you want to know if two things are equal we just look at the quality of their predecessors. So I'm going to implement that. So this is the number class and just add equals equals they take another number in and all we do here is you say is our predecessor equal to the predecessor of the other number. But to make this work I just need to add a quick attribute reader method which is pred. Okay but then that's it I can go down and run the code and it gives us the right answer. One is indeed equal to one and one is not equal to two. That looks like it works it's a bit of a problem. If I can say one is equal to two I actually will have flipped that round and say okay is two equal to one and it should come back and say false. When we run the code it says ooh exception it's all gone wrong. So what's happened here? Well it helps to think through the code we just wrote. To compare two to one I'm saying okay I'm going to look at the predecessor of both houses of that. So the predecessor of two is one I'm going to say is that equal to zero? Now to work with that out we look at the predecessor again we say is the predecessor of one which is zero equal to or what the predecessor of zero we don't have a concept of that yet and this is why it's breaking. So to make this work to go back to our zero class we add a new method called predecessor. So what is the predecessor of zero? Well I guess we've got integers here so we could say maybe it's negative one but we don't have negative numbers here we haven't built those things. Now it would take me a little while and actually it's quite unnecessary. The only thing I need to know about the predecessor of zero is that it's less than zero so I can just do that. I can say it's a new instance of less than zero. Okay, now we go back up here obviously we need to find that class so we'll find less than zero. The only thing I really care about here is equality and equality all works off by checking to see whether or not something is zero so the only method I need to add here is zero question mark and we just return false. So down here on the code again you can see this now works we can check equality. Now there's another little problem if I say is four equal to one? Run it, it blows up again because it gets all the way down to less than zero and called predecessor or not. So how do we define predecessor for less than zero? Well again this is actually quite easy. We could worry about things like is it minus two how does it compare to other numbers but actually I can just say that the thing the predecessor of something that's less than zero is still less than zero that's all I care about at this point. I'm going to just return self. Down here run the code again you can see it now works. So equality is now working for us and we put this together it means we can write things like this we can say one plus one is equal to two. I can run the code and it comes back and says true. Now this might look a bit funky because I've got this inline plus and equals but actually it's just sending messages it's just pure OO code. So for example I can rewrite this by just sending the message plus passing in the value one sending the message equals equals to the result of that passing the value two run the code it comes back and says true as you'd expect because it's exactly the same code. Now this is kind of amazing we just rebuilt numbers in pure OO way we don't need some prior definition of numbers or how they work. There are lots of other things in Ruby we could look at but I don't have time to do everything the thing I want to look at next is this concept of if. Now we saw this in Sandy Meister's talk and she said we don't need if statements and it's probably true but I quite like having them. I'd like to be able to use some if like construct in my code. So how do we make this work? If you remember this Boolean class we have before we've got true and false. Now I would like to write some Ruby S code I would like to say something like if true then return to value one otherwise return to value two and then that's it. So I'm going to turn this into some pure OO code and get that working. Now we can just translate this pretty easily we can create a new class called if we'll instantiate it we'll pass in the concept of true and we'll say okay well if it's true then we'll return one otherwise we'll return two. Okay so that's our pure OO equivalent of the Ruby code above so we can chart that away now we don't need it. So we need to define if and how is this going to work? Well actually if is pretty simple to write. You can see that it takes a single argument this is either going to be true or false so I'm going to pass this in to the initialiser and I'm just going to call it the conditional and the conged and for now I'm just going to remember it. The next thing we need is a method called then and then it's going to take some kind of value. Now if the conditional is true then we want to return that value otherwise we want to wait for the else clause. This is a bit of a problem how do we write if when to define then we also need if we get this kind of circular problem. It turns out it's really easy to solve we don't solve it here we just pass it down to the next object. So if it just becomes a wrapper around the conditional we just call then and rely on that to do the right thing. So this means we need to define then for true and false. Okay so inside true we'll take a value and if the conditional is true that's the value we return we should just return this and it's kind of tempting to just say okay that's what we'll do we'll return it but if you think back to the example we had above on top of the file you can see that after we call then we then call else so whatever we return has to respond to else and of course our value in this case one doesn't return to else it's not elseable so we need to return something else that wraps up our value but lets us call else. I'm going to do that by just creating a new class I'm going to call it true result and I'm going to pass in the value that we were given. Okay to find this class is really short. Go to result it's got a single, oh in the initialiser you can see we passed in the value okay so I'm just going to store that for now and then we need a single method called else and that takes an argument but here we don't actually care about it this is a true result so we're going to use the then branch so the else branch we don't care about it we just throw the value away and we'll return the original value we were given okay and down here for false we'll do the same thing obviously the logic is going to be reversed so we have then takes an argument we don't care about it we'll return a new false result I'll just find that quickly there's no initialiser here we just need a single method called else takes a value and again it's just identity function it just returns that thing okay so let's see this in action okay so here's our if statement that we built and they're pure away we're going to do just look at the result of this so assign it to variable and then I will run the code and see what it produces and it does the right thing we say if true then 1 1 is what is returned is 0,4 by a signal dash and I can change this to say if false and run the code and you can see it now returns the else branch it returns 2 so it looks like we solved if but actually we're not quite done yet it's a bit of a problem and to see this I'm going to write a slightly longer piece of code imagine we've got some code and inside it we're storing something called a threat level and during the execution of a code just get set to the value 1 now based on the threat level we want to decide some kind of action so you might say ok our action is equal to well if the threat level is equal to 4 then we're going to launch some kind of preemptive strike here so we'll say launch nuclear missile bit extreme otherwise, well this is a pretty creepy piece of code actually we'll do nothing we'll just take no action and then we run this code and we look at the result and unsurprisingly it says ok we take no action because our threat level is only 1 now let's write the same thing in a pure OO way so again we can set the threat level but we'll set it to our OO version of 1 this time and we can say the action that we take is equal to our new if construct and here luckily we can say is the threat level equal to 4 if it is then we'll launch our nuclear missile again nuclear missile is really hard to type never pick this for an example ok otherwise we'll take no action so we need a class to represent no action no action it's not going to do anything we'll just get it to print out something useful so we can see what's going on ok and then this is our pure OO version so we look at the value of action when we run it it's ooo what's happened here it's all gone wrong in fact it says uninitialised constant nuclear missile and that's kind of weird it's like it tried to run the den branch but we just saw that when the value is fault which is what comes out with 1 equal to 4 that it doesn't run the den branch it runs the else so what's happening well actually our if code is fine it's something else going on here it's Ruby's fault Ruby's got this thing called eager evaluation and when it sees a method called like then and then some expression between the parentheses it tries to be really helpful and it goes and evaluates this thing for us and it gives us a result which means that even though we didn't use the value for then it goes and evaluates the nuclear missile code and then we throw it away and that's really not what we want to do it's all this in Ruby by using a block you use a block to delay the execution but I can't do that I don't have blocks so how do I delay execution here well actually the answers are always really easy there's not much I can do what I'm going to do is create a new class and I'm going to call this class option 1 I'm going to give it a single method called result and inside here that's where I'm going to put my nuclear missile at new launch code and then that's it and I'll create another class called option 2 and again I'm going to give it a single method called result and inside here this is where we're going to do our noaction.new let me change the code in here so rather than actually launching a new commercial straight away we're just going to create a new instance of option 1 and then in the else part we're going to create a new instance of option 2 okay now we run our code it says it says I've made a typo somewhere anyone got any ideas something's coming in as a fault this is the example something's coming in as a fault this is the thing I get all the time when I type a let me just delete that code so class called option 1 single method called result and it says nuclear missile.new.launch and then down here new class called option 2 method called result it's going to be noaction.new does that help no okay I'm going to skip over this bit sorry not yet it should give me back the option class first and you can call result in it line 7 is going to be a lie somewhere else these things are horrible to debug I'll just have typoed somewhere sorry no it's not result yet I'm going to call result if this worked have a call result later what's going to happen is it's going to give me back either an instance of option 1 or an instance of option 2 and based on that I'm going to have the wrong thing so I would call result based on that and get the internal thing out of there and use that now getting it to work isn't actually that important what's really interesting here I think is we've had to stop and step outside of our pure OO model and think about the underlying thing think about what's happening behind the scenes we had to think about the machine underneath us now the machine in this case is a ruby interpreter and that's forced some constraints on the way we've written our code normally I would say at this point but it works, obviously here it doesn't work but imagine it did ok I'm going to move on from here I'm going to look at something else, I'm going to look at strings now strings are kind of a context thing because they're actually two different concepts ok strings are lists of characters so we need to solve lists and we also need to solve characters now I don't have time to do both but I've already written a list class in a pure OO way so I'm going to look at characters because something interesting happens here ok so I know I want to create a character class and what is a character? well it's really just a representation of some kind of thing that we see printed out and typically we use codes to represent this and in this case I'm going to use ASCII codes so I'm going to add an initialize method here it's going to take a code I'm just going to store it in a variable for now then an inspect method I'm just going to inspect that code ok and then that's it that's all I'm going to do so to make this work or to use it I can create for example the character H and I said these are going to take ASCII codes so the ASCII code for this is 104 and E again is going to be a new character and this time it's going to be 101 L is a new character it's going to be 108 I hope and O is a new character which is going to be 111 ok so using this I can now start to build a string I can say a string is equal to list.new now list is the pure OO version of lists I built and the way it works is you instantiate it then you stick things onto the end of it so I just append H onto there and then E and L and then L again and then O then we look at the string we should say hello and there we go perfect that's not very useful is it now it does work I hope you can see I include this class called more numbers that's because I needed to find some really big numbers ok so I just call successor this class file just really sucks an awful lot but it does have some useful utility methods inside there I can turn my pure OO numbers back into normal Ruby numbers by calling to I then I can call character inside there and that's going to turn it back into the Ruby representation of the character so if I run that you can see it does print out hello but to get that out I had to cheat I had to use built in Ruby things that are not part of my OO language we put it back to inspect we get this horrible thing come out again and this is not very useful and this raises an issue about strings and characters because they're not just representations inside the machine they're actually things we use to communicate with the outside world and we can't really talk about strings we like talking about input outputs now input output if you go to Wikipedia this definition of it it says it's a communication between a computer and the outside world I think this is a pretty good informal definition of IO now this does cause some problems for me because it's got this concept of the outside world this doesn't mean I need to model the outside world somehow well I can model the outside world because it doesn't fit into my rules it's not an object and this means that I can't send messages to it and this causes me problems because it turns out that our OO language, IO, is impossible it's impossible because it needs a concept of the outside world and you can't represent that in a simple object this makes me sad and it makes me a little bit worried as well because we've been doing pretty well so far well in the run-through they're pretty well we can do numbers, we can do some conditionals we can do characters and strings to some extent but we find out that we have a failure we can't do input output and this raises an awkward question what else can we do in normal Ruby that we can't do in our pure OO version well handily the answer is not to be nothing there's nothing we can do apart from IO in normal Ruby that we can't also do in our pure OO version and that's cool but how do I know it's true I haven't written every single Ruby program and converted it into my pure OO thing and yet I'm confident to stand here in front of 500 people and say there is nothing apart from IO that you can do in normal Ruby that you can't do in this pure OO version so how do I know it's true it might be a good point to admit that this isn't really a talk about OO programming it's about something much more fundamental something much deeper now to answer this question about how I could be sure there was nothing we could do in Ruby that we couldn't do in our pure OO version I went back and looked at a question I skipped over earlier I probably should have stopped and asked I said something like this you can't have pure OO numbers and I said this is untrue and I knew this was untrue as soon as I said it but how do I know it was untrue I've never seen someone build this kind of thing before so how do I know it wasn't true well there are two possibilities here either I'm some kind of crazy genius I can look at the problem and think oh here's all the code that solves it or I copied it which one do we think it's going to be genius or copy it come on genius now it always happens I never get to be the genius it sucks so I've seen it before but where have I seen it before I've never seen someone do this kind of talk before so where have I seen this kind of pure OO numbers before well let's stop for now let's park this thought for a minute take a little diversion and talk about lambda calculus now lambda calculus is one of these things that a lot of people find kind of scary they think oh it's fundamental computer science it's not for me it's too hard and actually lambda calculus is one of the simplest things you'll ever come across there are only three parts to lambda calculus three types of things you can have so you can have variables things like x and y and later it can be longer than this and these act like variables in your normal programming language you can assign things to them when you use them it'll use the original value that you assign to it later on now the next thing that looks a bit scary these things are called lambda obstructions look at this funny lambda symbol and then a variable then a dot and then a body now in this case the body is just the same as the thing that comes before the dot that doesn't have to be the case it can be any other valid lambda expression in there now at lambda obstructions we typically take so here we could say id is equal to lambda x dot x okay now lambda obstructions actually are just functions we can write the same thing in Ruby by saying def id takes a single argument of x now this is the thing that comes before the dot then the method body is everything that comes after the dot in this case it's just a simple x okay the final thing is lambda application now this is two lambda terms side by side a lambda term is just one of the three things we've seen so it's a variable and another application application is a bit like calling method it's a bit like calling the method t and passing in the value s that's great why am I telling all this why do we care about it terms like lambda calculus is really powerful and we saw in the last talk that we can build lists with it we can also do some kind of boolean logic with it we can also do numbers we can do all sorts of things and in fact lambda calculus is chewing complete which means that anything we can do in Ruby we can do in lambda calculus it's really funny I'm sure we're all very happy for lambda calculus but why am I telling you this rather than shutting up and letting you go for coffee well here's what gets interesting for me in our pure over Ruby we can implement the lambda calculus which means it's chewing complete which means it can do everything that Ruby can do except for I O that's pretty cool I want to show you what I mean by saying it can implement the lambda calculus so we had some lambda term we had something like id equals lambda x dot x we can mechanically turn this into some pure over Ruby code and the rules are as follows when you see a lambda abstraction this funny lambda symbol I'm going to create a new class I give this class the same name as the variable this thing is stored in so in this case it's stored in a variable called id now I'll always create a single method inside here called call this takes a single argument in this case I'm going to call the argument x to match the lambda abstraction above and then the body just follows the thing after the dot so I'll return an x and that's it now this doesn't just work for simple things like the identity function we can look at something a bit more complex so in lambda calculus we could say true is equal to lambda t dot lambda f dot t now you don't necessarily need to worry about why that's the case I just want to show you that this is more complex but we can follow the rules and convert it ok so the rule says we create a class we give the same name as a variable it has a single method called call it takes a single argument in this case t to match the thing above now the body of this is a little bit more complex before we set a simple variable we just wrote the variable name and carried on here it's another lambda abstraction but that's ok because we have the rules we'll just follow the same rules now this inner lambda abstraction is not assigned to a variable so I'm going to use an anonymous class inside here very class.new.do ok and inside here we follow the same rules we have a single method inside here called call takes a single argument in this case f and then we want to return t now we have a bit of a problem as soon as we created the new class we broke the outer scope the outer scope is no longer available to us so we can't just use t so what we're going to do instead is add an initializer inside here I'm going to pass in the value t and remember it and then down in our definition of call we can simply return that value then we'll instantiate it straight away and pass in t so it has access to it ok so this means t.new then we can call call on it I'm just going to pass in the symbol t for now then we can call it again and pass in the symbol f and hopefully this works it'll come back and say t this is the first time you've passed into it now you might notice this looks a little bit similar to the way we defined true before we can say true then something else something else and that's no coincidence these are both concepts of true this is great we can take the lambda calculus and turn it into pure or ruby we can do this with other fundamental forms of computation like Turing machines or iota or all these kind of weird things I haven't got time to talk about those now but if you are interested in this I really recommend you read this book understanding computation by Tom Stewart he goes through a lot of these different forms of computation and how they're related and all the examples are in ruby so you can follow along of course you might not be interested and if you're not interested in this I recommend you read this book by Tom Stewart understanding computation because it's a really great book and it may get you excited about it so we can take lambda calculus we can put it into pure or ruby and then we know it's Turing complete but a bit of a problem lambda calculus is really hard to use it's a very simple concept but it's hard to get it to do things so here's an example of 1 plus 1 is equal to 2 this is the same thing written out in lambda calculus now we wrote this out in our pure or ruby earlier and this is the same thing and it's horrible I don't really care that it's long and scrolls off the page what I care about is that I would hate to work with this it's really really hard to see what's going on you get blinded by all these funny symbols on the page and it's really hard to work out the intention so much of the code is concerned with just trying to get the thing to work that the original intention of the code is completely lost so when I wrote my pure or ruby version I didn't just take lambas and wrap it off inside objects I translated them into better or ruby codes because even though our pure old thing in lambda calculus are computationally equivalent they can both solve the same sets of problems they're not equally expressive a quick sub by side example we have the value 1 in lambda calculus it's defined as lambda s dot lambda z dot s z if you want to get the value 2 we use the successor function which is lambda n dot lambda s dot lambda z dot s of n s z okay now I know this works and I've tried this several times but it's horrible I would hate to work with something like this every day we've got the same thing in ruby you can say look one is just the number that comes after zero and two is just the number that comes after one and we get to feel very smug about this because this is much more expressive this is a ruby conference not a lambda calculus conference so we get to be the guys with a great language this feeling of smugness is nice but it's also kind of fleeting because it's not going to be long and it comes along and says look just use two that's the right answer all this thing we were doing with number dot new is just a distraction this happens all the time we think hey we can use this really cool thing to solve a problem I mean it confused into thinking this means we should use this thing to solve a problem and I think the reason we make this mistake so often is because we look at these things as systems of rules and we think rules are there to be obeyed we must always follow them and the calculus I gave you some rules for my pure ruby I gave you some rules and you think I must follow these rules but it applies to many things it applies to things like the null object pattern we think these are rules that must be obeyed or solid principles we think these are rules we must follow them or microservices or whatever else and they're not rules that we must follow they're just tools to help us do a job and they might not be the best tools for the job you might be better off rethinking the problem and solving it in a very different way and I do this all the time and I catch myself doing it and I go from thinking hey I'm some super smart developer I can solve any problem in lambda calculus to realising that I'm just the worst kind of idiot I just don't know what's appropriate so I'm not saying you shouldn't care about lambda calculus and that you shouldn't try and build pure OO objects because there is something really amazing about the computational power of lambda calculus and I think there's something really amazing about the computational power of pure OO Ruby I've practised this talk so many times and every time I can rebuild arithmetic just by sending messages I'm amazed by it I think there's something really amazing about the fact that lambda calculus and our pure OO Ruby have got the same computational power I'm saying these things, what I'm really saying is that there's something really amazing about programming I learned a programme a long time ago when I learned a programme it was on one of these I think I said expectrum and one of the ways you learn to programme on these is you buy magazines now you probably don't know magazines but it is like printed out things like printed out webpages and you could flick through the magazine and there would be a programming section inside there and it would have a description of the problem and then a code listing and this code listing is on the right here and it goes on for a couple of pages and what I would do as a kid is sit there and copy this thing out diligently retype it all and if I hadn't made too many typos like I did earlier I would run the code at the end and I'd go well that's kind of cool then I'd go back and fiddle with it a bit tweak it a bit and initially I'd do something kind of boring I'd get it to print out like hello John at the beginning but after a while I would do more interesting things I'd change some of the values to make it work in a different way or change the control flow or I'd take some statements out or put some new statements in well I came to realise there's a few rules here that go over and hide this thing works and once I'd learned those rules I could use them to create this endless array of programmes this is an amazing thing it means I can take these ideas, these things in my head and I can follow the rules and turn them into code I can feed this code into the machines and the machines that add electricity to them and in that moment they become alive and in that moment I become like Frankenstein and these are my creations and it is an amazing feeling and it's the same idea that's to create the most amazing pieces of software today and the same idea that lets it touch the lives of thousands of people and it all comes from these simple ideas it all comes from following a few simple rules and I think that this is part of what it means to be a programmer to have this realisation to understand that the few fundamental relations being true certain other combinations of relations must have necessity follow combinations unlimited in variety and extent this is a quote from Ada Lovelace it was written in raw in 1842 what I find really amazing about Ada Lovelace it's not that she was the first programmer I mean anyone could have done that it's that she realised this over a hundred years before the first computers were built she saw this fundamental truth about computing she saw this fundamental beauty in programming and this power in what we do and it's these ideas that got me interested in programming in the first place and it's these same ideas this fundamental truth about the powerful beauty of programming that mean I still love doing it today my name is Joel and I've been programming for 25 years and this is why I love programming thank you very much